Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d02e10c
custom finch name
nickolaich Nov 15, 2023
6729f13
Merge pull request #1 from nickolaich/custom_finch_name
nickolaich Nov 15, 2023
b2f849f
add locations for country by service
nickolaich Nov 15, 2023
4e35c27
Merge pull request #2 from nickolaich/serp-locations
nickolaich Nov 15, 2023
7aec373
allow empty timeouts
nickolaich Nov 16, 2023
856b7d5
Merge pull request #3 from nickolaich/change-config-reading
nickolaich Nov 16, 2023
1c8c413
added more api for google trends and google ad
nickolaich Nov 25, 2023
c8e2c1c
moved fixtures into json files under proper API tree
nickolaich Nov 25, 2023
6dd4314
Merge pull request #4 from nickolaich/google_trends_ads_api
nickolaich Nov 25, 2023
7c00066
added keyword research fixtures and search intent API implemented
nickolaich Nov 25, 2023
cdd2de0
keywords for site endpoint support
nickolaich Nov 25, 2023
175e299
related keywords endpoint
nickolaich Nov 25, 2023
8ac744d
fixed comments
nickolaich Nov 25, 2023
b626db2
keyword ideas endpoint
nickolaich Nov 25, 2023
f90f7f3
keyword suggestions
nickolaich Nov 25, 2023
39500a3
added google trends explorer API support
nickolaich Nov 25, 2023
119779d
Merge pull request #5 from nickolaich/lab_research_keywords_api
nickolaich Nov 25, 2023
11cdf18
serp data model for google and translators
nickolaich Nov 26, 2023
ee363f6
search intent data model
nickolaich Nov 26, 2023
3c805e5
keywords for site data model
nickolaich Nov 26, 2023
8d4cf3d
keyword ideas
nickolaich Nov 26, 2023
56ebb0c
keyword ideas
nickolaich Nov 26, 2023
937fa7b
keyword suggestions
nickolaich Nov 26, 2023
5dac6fb
related keywords
nickolaich Nov 26, 2023
4567836
related keywords
nickolaich Nov 26, 2023
e89be8e
keyword difficulty
nickolaich Nov 26, 2023
0b24d6b
lab categories
nickolaich Nov 26, 2023
040d869
bootstraped google trends data
nickolaich Nov 26, 2023
be06c1e
google trends explorer data model
nickolaich Nov 27, 2023
d4bf1a9
added google trends loc/lang/categories
nickolaich Nov 27, 2023
28d7636
Merge pull request #6 from nickolaich/entity-data-model
nickolaich Nov 27, 2023
d268965
fixed parsing date if already ISO format
nickolaich Nov 27, 2023
e1c85de
made response factory public available to use in project's tests
nickolaich Nov 27, 2023
dff7270
configurable fixtures path
nickolaich Nov 27, 2023
06069ef
fixed payload wrap for the task
nickolaich Nov 28, 2023
294e76c
handle nil result properly
nickolaich Nov 28, 2023
1a16c5d
bing and youtube organic searches api
nickolaich Dec 20, 2023
ce39fa8
fixed tests and youtube serp path
nickolaich Dec 20, 2023
19b8485
bump ecto version
nickolaich Oct 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ erl_crash.dump
# Ignore package tarball (built via "mix hex.build").
data_for_seo-*.tar

.idea/
.DS_Store
test/.DS_Store
109 changes: 109 additions & 0 deletions lib/data_for_seo/api/endpoint_handler.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
defmodule DataForSeo.API.EndpointHandler do
@moduledoc """
Basic module with some shared functionality.
"""

defmacro __using__(_) do
quote do
alias DataForSeo.Client
alias DataForSeo.Parser

@type generic_response :: {:ok, any()} | {:error, any()}

defp post_task(endpoint_url, payload) when is_map(payload) do
# wrap a single task
post_task(endpoint_url, [payload])
end

defp post_task(endpoint_url, payload) when is_list(payload) do
endpoint_url
|> Client.post(payload)
|> Client.validate_status_code()
|> Client.decode_json_response()
|> case do
{:ok, resp} ->
{:ok, Parser.parse(resp, :task_post)}

{:error, error} ->
{:error, error}
end
end

defp get_live_task(endpoint_url, payload) when is_map(payload) do
# wrap a single task
get_live_task(endpoint_url, [payload])
end

defp get_live_task(endpoint_url, payload) when is_list(payload) do
endpoint_url
|> Client.post(payload)
|> Client.validate_status_code()
|> Client.decode_json_response()
|> case do
{:ok, resp} ->
{:ok, Parser.parse(resp, :task_result)}

{:error, error} ->
{:error, error}
end
end

defp get_ready_tasks(endpoint_url) do
endpoint_url
|> Client.get()
|> Client.validate_status_code()
|> Client.decode_json_response()
|> case do
{:ok, resp} ->
{:ok, Parser.parse(resp, :tasks_ready)}

{:error, error} ->
{:error, error}
end
end

defp get_one_task(endpoint_url) do
endpoint_url
|> Client.get()
|> Client.validate_status_code()
|> Client.decode_json_response()
|> case do
{:ok, resp} ->
{:ok, Parser.parse(resp, :task_result)}

{:error, error} ->
{:error, error}
end
end

defp handle_response(resp), do: resp

# Apply location to payload from params
# nil value skip adding
# otherwise it must be valid numberic location code or any valid location name
defp apply_location(attrs, nil), do: attrs

defp apply_location(attrs, loc) do
# test location, if it's integer - we have location code
# we support code both: as int and string
case Integer.parse("#{loc}") do
:error -> Map.put(attrs, :location_name, loc)
{code, _} -> Map.put(attrs, :location_code, code)
end
end

# Apply language to payload from params
# nil value skip adding
# otherwise it must be 2-char lang code or any valid language name
defp apply_language(attrs, nil), do: attrs

defp apply_language(attrs, <<code::binary-size(2), "">>),
do: Map.put(attrs, :language_code, code)

defp apply_language(attrs, lang_name), do: Map.put(attrs, :language_name, lang_name)

defp apply_tag(attrs, nil), do: attrs
defp apply_tag(attrs, tag = <<_::binary-size(1), _::binary>>), do: Map.put(attrs, :tag, tag)
end
end
end
27 changes: 27 additions & 0 deletions lib/data_for_seo/api/keywords/google_ads/languages.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule DataForSeo.API.Keywords.GoogleAds.Languages do
@moduledoc """
Provides API interfaces to Keyword Data API / Google Ads / Languages :
https://docs.dataforseo.com/v3/keywords_data/google_ads/languages/
"""

alias DataForSeo.Client

@doc """
Gets all languages for google ads
## Examples
DataForSeo.API.Keywords.GoogleAds.Languages.get_all_languages()
"""
@spec get_all_languages(Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_languages(opts \\ []) do
Client.get("/v3/keywords_data/google_ads/languages")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end
end
27 changes: 27 additions & 0 deletions lib/data_for_seo/api/keywords/google_ads/locations.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule DataForSeo.API.Keywords.GoogleAds.Locations do
@moduledoc """
Provides API interfaces to Keyword Data API / Google Ads / Locations :
https://docs.dataforseo.com/v3/keywords_data/google_ads/locations/
"""

alias DataForSeo.Client

@doc """
Gets all locations for Google Ads
## Examples
DataForSeo.API.Keywords.GoogleAds.Locations.get_all_locations()
"""
@spec get_all_locations(Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_locations(opts \\ []) do
Client.get("/v3/keywords_data/google_ads/locations")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end
end
27 changes: 27 additions & 0 deletions lib/data_for_seo/api/keywords/google_trends/categories.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule DataForSeo.API.Keywords.GoogleTrends.Categories do
@moduledoc """
Provides API interfaces to Keyword Data API / Google Trends / Categories :
https://docs.dataforseo.com/v3/keywords_data/google_trends/categories/
"""

alias DataForSeo.Client

@doc """
Gets all categories for Google Trends
## Examples
DataForSeo.API.Keywords.GoogleTrends.Categories.get_all_categories()
"""
@spec get_all_categories(Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_categories(opts \\ []) do
Client.get("/v3/keywords_data/google_trends/categories")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end
end
74 changes: 74 additions & 0 deletions lib/data_for_seo/api/keywords/google_trends/explorer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
defmodule DataForSeo.API.Keywords.GoogleTrends.Explorer do
@moduledoc """
Provides API interfaces to Keyword Data API / Google Trends / Explore :
https://docs.dataforseo.com/v3/keywords_data/google_trends/explore/task_post/
"""

use DataForSeo.API.EndpointHandler

@doc """
This endpoint will provide you with the keyword popularity data from the ‘Explore’ feature of Google Trends.
You can check keyword trends for Google Search, Google News, Google Images, Google Shopping, and YouTube.
This is the Standard method of data retrieval. If you don’t need to receive data in real-time,
this method is the best option for you. Set a task and retrieve the results when our system collects them.
Execution time depends on the system workload.

See https://docs.dataforseo.com/v3/keywords_data/google_trends/explore/task_post/ for available prameters.

## Examples

```
DataForSeo.API.Keywords.GoogleTrends.Explorer.task_post(["seo api", "rank_api"], "Kyiv,Ukraine", "en", %{})
```
"""
@spec task_post([String.t()], String.t() | nil, String.t() | nil, map()) :: generic_response()
def task_post(keywords, loc_code_or_name, lang_code_or_name, extra) do
payload =
%{keywords: keywords}
|> apply_location(loc_code_or_name)
|> apply_language(lang_code_or_name)
|> Map.merge(extra)

post_task("/v3/keywords_data/google_trends/explore/task_post", payload)
end

@doc """
Gets result for a single task in a live mode: post it and retrieve result at the same time.
Read more: https://docs.dataforseo.com/v3/keywords_data/google_trends/explore/live/
## Examples
DataForSeo.API.Keywords.GoogleTrends.Explorer.task_live("test-task-id")
"""
@spec task_live([String.t()], String.t() | nil, String.t() | nil, map()) :: generic_response()
def task_live(keywords, loc_code_or_name, lang_code_or_name, extra) do
payload =
%{keywords: keywords}
|> apply_location(loc_code_or_name)
|> apply_language(lang_code_or_name)
|> Map.merge(extra)

get_live_task("/v3/keywords_data/google_trends/explore/live", payload)
end

@doc """
Gets the list of completed tasks ids.
Read more: https://docs.dataforseo.com/v3/keywords_data/google_trends/explore/tasks_ready/
## Examples
DataForSeo.API.Keywords.GoogleTrends.Explorer.tasks_ready()
"""
@spec tasks_ready() :: generic_response()
def tasks_ready do
get_ready_tasks("/v3/keywords_data/google_trends/explore/tasks_ready")
end

@doc """
Gets result for a single task.
The fetched tasks are then removed from completed list. So be sure to save and process them.
Read more: https://docs.dataforseo.com/v3/keywords_data/google_trends/explore/task_get
## Examples
DataForSeo.API.Keywords.GoogleTrends.Explorer.task_get("test-task-id")
"""
@spec task_get(String.t()) :: generic_response()
def task_get(task_id) do
get_one_task("/v3/keywords_data/google_trends/explore/task_get/#{task_id}")
end
end
27 changes: 27 additions & 0 deletions lib/data_for_seo/api/keywords/google_trends/languages.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule DataForSeo.API.Keywords.GoogleTrends.Languages do
@moduledoc """
Provides API interfaces to Keyword Data API / Google Trends / Languages :
https://docs.dataforseo.com/v3/keywords_data/google_trends/languages/
"""

alias DataForSeo.Client

@doc """
Gets all languages for google trends
## Examples
DataForSeo.API.Keywords.GoogleTrends.Languages.get_all_languages()
"""
@spec get_all_languages(Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_languages(opts \\ []) do
Client.get("/v3/keywords_data/google_trends/languages")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end
end
46 changes: 46 additions & 0 deletions lib/data_for_seo/api/keywords/google_trends/locations.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
defmodule DataForSeo.API.Keywords.GoogleTrends.Locations do
@moduledoc """
Provides API interfaces to Keyword Data API / Google Trends / Locations :
https://docs.dataforseo.com/v3/keywords_data/google_trends/locations/
"""

alias DataForSeo.Client

@doc """
Gets all locations for google trends
## Examples
DataForSeo.API.Keywords.GoogleTrends.Locations.get_all_locations()
"""
@spec get_all_locations(Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_locations(opts \\ []) do
Client.get("/v3/keywords_data/google_trends/locations")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end

@doc """
Gets all locations for google trends
## Examples
DataForSeo.API.Keywords.GoogleTrends.Locations.get_all_locations_by_country("ua")
"""
@spec get_all_locations_by_country(String.t(), Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_locations_by_country(country_code, opts \\ []) do
Client.get("/v3/keywords_data/google_trends/locations/#{country_code}")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end
end
27 changes: 27 additions & 0 deletions lib/data_for_seo/api/labs/google/categories.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
defmodule DataForSeo.API.Labs.Google.Categories do
@moduledoc """
Provides API interfaces to Labs / Google / Categories API:
https://docs.dataforseo.com/v3/dataforseo_labs/categories_list/
"""

alias DataForSeo.Client

@doc """
Gets all locations by country for specific service: bing, google, youtube etc
## Examples
DataForSeo.API.Labs.Google.Categories.get_all_categories()
"""
@spec get_all_categories(Keyword.t()) :: {:ok, map()} | {:error, term()}
def get_all_categories(opts \\ []) do
Client.get("/v3/dataforseo_labs/categories")
|> Client.validate_status_code()
|> Client.decode_json_response(opts)
|> case do
{:ok, resp} ->
{:ok, resp}

{:error, error} ->
{:error, error}
end
end
end
Loading