Google Ads keyword research
Search volume, competition and bid ranges from Google's Keyword Planner, for keyword ideas or a list you already have.
/v1/google-ads/keyword-ideasFree (daily caps apply)API key + connected Google Ads accountThe /v1/google-ads/* routes expose Google Ads keyword planning for the Google Ads account you connect to RankParse. Use keyword-ideas to discover new keywords from seed terms or a page URL, and keyword-metrics to size a list of keywords you already have.
Volume and competition only, never rankings. Google Ads has no concept of organic search position. For "where do I rank?" use Google Search Console (live, Google only) or rank tracking (stored snapshots across Google, Bing and Yandex).
Manager (MCC) accounts are not supported yet. Keyword planning must target a regular Google Ads account. A login that only manages other accounts sees just its manager ID, and Google rejects keyword planning against it. Connect with a Google login that has direct access to a regular Ads account.
Before you call
Connect Google Ads
In the dashboard open Integrations, choose Google Ads and click Connect. Approve the Google consent screen with the Google login that can access your Ads account.
Choose an account
If your login can access exactly one Ads account, RankParse selects it for you. If it can access several, pick one from the Account dropdown on the Google Ads card. Keyword calls will not run until an account is selected (google_ads_no_account_selected).
Call the API
Send POST /v1/google-ads/keyword-ideas or POST /v1/google-ads/keyword-metrics with your API key.
Cost and daily caps
These routes cost 0 credits, so the daily caps below are the only limit on how much you can use them.
| Cap | Limit | Error code |
|---|---|---|
| Per user, per UTC day | 100 requests | google_ads_user_quota_exceeded |
| All RankParse users together, per UTC day | 10,000 requests | google_ads_daily_quota_exceeded |
- Counting is per request, not per keyword. A
keyword-metricscall with 200 keywords counts as one. - A request is counted only after Google answers it successfully. Failed calls do not use up your cap.
GET /v1/google-ads/accountsalso calls Google and counts as one request.- Both caps reset at midnight UTC. The 429 body includes
retry_after, the number of seconds until then. - Google enforces its own limit on top of these; hitting it returns
google_ads_quota_exceeded. - Requests are also subject to the shared 60 requests per minute rate limit (see Rate limiting).
POST /v1/google-ads/keyword-ideas
/v1/google-ads/keyword-ideasFreeAPI key + connected Google Ads accountDiscovers new keyword ideas from seed keywords, a URL, or both, each with average monthly searches, competition and top-of-page bid range.
Body parameters
Send JSON with Content-Type: application/json.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
keywords | string[] | One of keywords or url | Seed keywords. Blank entries are ignored and only the first 20 are used | |
url | string | One of keywords or url | A page or site URL to derive ideas from. If both keywords and url are sent, Google uses them together | |
language | string | No | languageConstants/1000 | Google language resource name. 1000 is English. Must match languageConstants/<digits> |
geo_target_constants | string[] | No | ["geoTargetConstants/2840"] | Google location resource names. 2840 is the United States. Each must match geoTargetConstants/<digits>; only the first 10 are used |
keyword_plan_network | string | No | GOOGLE_SEARCH | GOOGLE_SEARCH or GOOGLE_SEARCH_AND_PARTNERS. Any other value falls back to the default |
include_adult_keywords | boolean | No | false | Include adult keywords. Only a literal true turns it on |
limit | integer | No | Google decides | Maximum ideas to return. Clamped to 1 to 1000 |
language and geo_target_constants are Google resource names, not bare IDs. Sending "2840" instead of "geoTargetConstants/2840" fails fast with a 400 and does not touch your cap. Google publishes the full lists of language and geo target IDs.
Example
curl -X POST "https://api.rankparse.com/v1/google-ads/keyword-ideas" \
-H "X-API-Key: rp_your_key" \
-H "Content-Type: application/json" \
-d '{
"keywords": ["seo api", "backlink checker"],
"geo_target_constants": ["geoTargetConstants/2840"],
"language": "languageConstants/1000",
"limit": 25
}'import requests
res = requests.post(
"https://api.rankparse.com/v1/google-ads/keyword-ideas",
headers={"X-API-Key": "rp_your_key"},
json={
"keywords": ["seo api", "backlink checker"],
"geo_target_constants": ["geoTargetConstants/2840"],
"language": "languageConstants/1000",
"limit": 25,
},
)
for idea in res.json()["data"]:
print(idea["keyword"], idea["metrics"]["avg_monthly_searches"])const res = await fetch("https://api.rankparse.com/v1/google-ads/keyword-ideas", {
method: "POST",
headers: {
"X-API-Key": "rp_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
keywords: ["seo api", "backlink checker"],
geo_target_constants: ["geoTargetConstants/2840"],
language: "languageConstants/1000",
limit: 25,
}),
});
const { data } = await res.json();Response
{
"data": [
{
"keyword": "seo api",
"metrics": {
"avg_monthly_searches": 1900,
"competition": "MEDIUM",
"competition_index": 47,
"low_top_of_page_bid_micros": 1850000,
"high_top_of_page_bid_micros": 6200000
}
}
],
"total": 1,
"total_available": 312,
"customer_id": "1234567890"
}Response fields
| Field | Type | Description |
|---|---|---|
data | array | Keyword ideas |
data[].keyword | string | The keyword text |
data[].metrics | object | Metrics for that keyword (see below) |
total | integer | Ideas in this response |
total_available | integer or null | Total ideas Google found, which can be larger than total |
customer_id | string | The Google Ads account the call ran against |
POST /v1/google-ads/keyword-metrics
/v1/google-ads/keyword-metricsFreeAPI key + connected Google Ads accountReturns metrics for an explicit list of keywords, with no expansion into related terms.
Body parameters
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
keywords | string[] | Yes | Keywords to look up. Blank entries are ignored. At most 200 are used; the rest are dropped and truncated is set to true | |
language | string | No | languageConstants/1000 | Same as for keyword ideas |
geo_target_constants | string[] | No | ["geoTargetConstants/2840"] | Same as for keyword ideas (max 10) |
keyword_plan_network | string | No | GOOGLE_SEARCH | GOOGLE_SEARCH or GOOGLE_SEARCH_AND_PARTNERS |
include_adult_keywords | boolean | No | false | Only a literal true turns it on |
Example
curl -X POST "https://api.rankparse.com/v1/google-ads/keyword-metrics" \
-H "X-API-Key: rp_your_key" \
-H "Content-Type: application/json" \
-d '{"keywords": ["seo api", "rank tracker"]}'import requests
res = requests.post(
"https://api.rankparse.com/v1/google-ads/keyword-metrics",
headers={"X-API-Key": "rp_your_key"},
json={"keywords": ["seo api", "rank tracker"]},
)
body = res.json()
if body["truncated"]:
print(f"Only the first {body['max_keywords']} keywords were looked up")const res = await fetch("https://api.rankparse.com/v1/google-ads/keyword-metrics", {
method: "POST",
headers: {
"X-API-Key": "rp_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({ keywords: ["seo api", "rank tracker"] }),
});
const { data, truncated } = await res.json();Response
{
"data": [
{
"keyword": "seo api",
"close_variants": ["seo apis"],
"metrics": {
"avg_monthly_searches": 1900,
"competition": "MEDIUM",
"competition_index": 47,
"low_top_of_page_bid_micros": 1850000,
"high_top_of_page_bid_micros": 6200000
}
}
],
"total": 1,
"requested": 2,
"truncated": false,
"max_keywords": 200,
"customer_id": "1234567890"
}Response fields
| Field | Type | Description |
|---|---|---|
data | array | One entry per keyword Google returned metrics for |
data[].keyword | string | The keyword text |
data[].close_variants | string[] | Close variants Google groups with this keyword. Empty when none |
data[].metrics | object | Metrics for that keyword (see below) |
total | integer | Entries in data |
requested | integer | Keywords actually sent to Google, after trimming to 200 |
truncated | boolean | true when you sent more than 200 keywords. Treat the missing ones as "not asked for", not "no data" |
max_keywords | integer | The per-call limit (200) |
customer_id | string | The Google Ads account the call ran against |
The metrics object
| Field | Type | Description |
|---|---|---|
avg_monthly_searches | integer or null | Average monthly searches. Google may report heavily rounded figures for accounts without meaningful ad spend, and the response does not say whether a figure is precise |
competition | string or null | Google's competition level for paid placement, for example LOW, MEDIUM or HIGH |
competition_index | integer or null | Competition on a 0 to 100 scale |
low_top_of_page_bid_micros | integer or null | Low end of the top-of-page bid range, in micros of the account currency (1,000,000 micros is one currency unit) |
high_top_of_page_bid_micros | integer or null | High end of the top-of-page bid range, in the same units |
Any metric can be null when Google does not return it. A per-month volume breakdown is not included.
GET /v1/google-ads/status
/v1/google-ads/statusFreeAPI keyShows whether Google Ads is connected and which account is selected. It makes no call to Google, does not count against your daily cap and has no MCP tool.
{
"connected": true,
"google_email": "you@example.com",
"connected_at": 1756112400000,
"selected_customer_id": "1234567890",
"login_customer_id": null
}When nothing is connected the response is { "connected": false }. selected_customer_id is null while no account is chosen. login_customer_id is reserved for manager-account support and is currently always null.
GET /v1/google-ads/accounts
/v1/google-ads/accountsFreeAPI keyLists the Google Ads accounts the connected Google login can access directly, as 10-digit customer IDs. It works before an account has been selected, and counts as one request against your daily cap.
{
"data": ["1234567890", "9876543210"],
"total": 2
}An empty list means Google returned no directly accessible accounts for this login.
Errors
All errors use the standard shape { "error", "code", "message" }. See Error codes.
| Status | Code | Meaning and fix |
|---|---|---|
400 | invalid_json | The request body is not valid JSON |
400 | missing_seed | keyword-ideas needs keywords and/or url |
400 | missing_keywords | keyword-metrics needs a non-empty keywords array |
400 | invalid_language | language is not a resource name like languageConstants/1000 |
400 | invalid_geo_target | geo_target_constants is not an array of names like geoTargetConstants/2840 |
400 | google_ads_not_connected | Google Ads is not connected. Connect it under Dashboard, Integrations |
400 | google_ads_no_account_selected | Connected, but no Ads account is selected. Choose one on the Google Ads card |
400 | google_ads_invalid_request | Google rejected the request, usually because of an invalid language or location. The message carries Google's detail |
401 | invalid_api_key | Missing, invalid or revoked API key |
401 | google_ads_token_expired | The Google Ads connection expired or was revoked. Reconnect under Dashboard, Integrations |
403 | capability_missing | Your workspace role cannot use this route |
403 | google_ads_developer_token_not_approved | RankParse's Google Ads developer token is not yet approved for live keyword data. Nothing is wrong with your account; this is pending on the RankParse side |
403 | google_ads_customer_not_enabled | The selected Ads account is not active (never fully set up, or deactivated). Select a different account or activate this one in Google Ads |
403 | google_ads_access_denied | Google denied the request. Most often the selected account is a manager (MCC) account. Select a regular Ads account |
404 | google_ads_account_not_found | The selected account was not found. Select it again on the Google Ads card |
429 | rate_limited | More than 60 requests per minute for this key |
429 | google_ads_user_quota_exceeded | You used your 100 requests for the UTC day. See retry_after |
429 | google_ads_daily_quota_exceeded | RankParse's shared daily budget is used up. Try again after midnight UTC |
429 | google_ads_quota_exceeded | Google's own Ads API quota was exceeded. Retry later |
502 | google_ads_token_refresh_failed | Could not refresh the Google access token. Retry shortly |
502 | google_ads_api_error | Any other upstream failure |
503 | google_ads_not_configured | The Google Ads integration is not configured on the server |
MCP tools
Available on the API-key MCP endpoint (mcp.rankparse.com) only, not on the secure OAuth connector. All are read-only and cost 0 credits, and every description tells the agent that results are volume and competition, not rankings. See MCP Server.
| Tool | Purpose |
|---|---|
get_google_ads_accounts | List accessible Google Ads accounts, to help pick one |
get_google_ads_keyword_ideas | Discover new keywords from seeds and/or a URL, with volume, competition and bid ranges |
get_google_ads_keyword_metrics | Volume, competition and bid ranges for a specific list of keywords |
The tools accept language, geo_target_constants and keyword_plan_network like the REST routes. include_adult_keywords is accepted by get_google_ads_keyword_ideas only.