AccuSERP API
Introduction
Welcome to the AccuSERP documentation. If you are looking for the AccuRanker API docs, please see here. AccuSERP is a dedicated API product for on-demand Google search results.
AccuSERP lets you queue keywords for Google search scraping and retrieve parsed
JSON results or raw HTML. Results are delivered asynchronously, either via a
callback URL you provide or by polling the GET /results endpoint with the
request ID. Heavy polling is throttled, so we recommend callbacks (see Callbacks).
Try it out! Click the Test Request button on any endpoint to send a live request directly from this page. If you're logged in, your API token is pre-filled automatically.
Recommended Flow
- Call
GET /enginesto pick a supported Google locale. - Submit a search with
POST /keywordswith acallbackURL. - Wait for the callback and
GET /resultswith the returnedid. - Save the result on your side before the storage TTL expires.
Authentication
AccuSERP uses API tokens for authentication. Your token must be included in every request.
Bearer Header (Recommended)
Pass the token in the Authorization header:
Authorization: Bearer your-auth-token
This is the recommended method!
Query Parameter (Legacy)
For backward compatibility, you can also pass the token as a query parameter:
/status?auth_token=your-auth-token
If both the header and query parameter are present, the header takes precedence.
You can find your token on the AccuSERP dashboard.
If the token is missing or invalid, the API returns a 401 response:
{
"detail": "Unauthorized"
}
Throughput
Your plan has a throughput limit: the maximum number of keyword requests you can submit within a rolling time window. The window is 1 hour unless your plan says otherwise. Your usage is counted across activity inside the current window. You can see your plan's throughput on the AccuSERP dashboard.
Certain endpoints do not count against your throughput. For example, you can call
GET /status to check your current usage without affecting your limit.
All rate-limited endpoints include the following response headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Your throughput limit for the current window |
X-RateLimit-Remaining |
Requests remaining in the current window |
Use these to keep an eye on your usage and slow down before you hit the limit.
If you exceed the throughput limit, the API returns a 429 response with a
Retry-After header telling you how many seconds to wait before retrying.
Please honor it — many HTTP clients can do so automatically:
{
"detail": "You have hit the rate limit of your requests"
}
Targeting a location
By default, AccuSERP queries Google with the locale you submit (e.g.
en-us). For finer control, you can override the search domain and language,
or pin the search to a specific city or coordinates. Pick one strategy at a
time. Mixing incompatible ones returns a 400.
Locale (default)
Pass a locale value from GET /engines. This sets the Google country
domain, language, and TLD as a single bundle. Most use cases stop here.
locale=en-us
Country, language, and TLD overrides
If you need a combination not in the locale list, override all three together:
gl=US&hl=en&tld=com
All three must be specified together. The tld must be a supported Google
TLD. When set, this combination
takes precedence over locale.
City or region
Pass a free-form geolocation string. Use this when the SERP should reflect a specific market.
geo=New York, NY, USA
geo cannot be combined with latitude/longitude or uule.
Precise coordinates
Pin the search to a latitude and longitude pair. Use this when you need sub-city precision.
latitude=40.7128&longitude=-74.0060
Both must be supplied together. Cannot be combined with geo or uule.
UULE
If you already have a Google UULE-encoded location string, pass it directly:
uule=w+CAIQICINTmV3IFlvcmssIE5ZLCBVU0E
uule cannot be combined with geo or latitude/longitude.
Credits
Credits & base requests. Your plan is sold in base requests on the AccuSERP dashboard, but the API charges in credits. The conversion is fixed: 1 base request = 10 credits. The dashboard, billing, and plan pages talk in base requests; everything below — including the
costobject returned byPOST /keywords— talks in credits.
A basic single-page scrape costs 10 credits (= 1 base request). Enabling
options adds a per-page surcharge; surcharges add together and then the total
is multiplied by num_pages:
| Option | Per-page surcharge |
|---|---|
js_rendering=true |
+ 10 credits per page |
priority=true |
+ 5 credits per page |
Formula: credits = 10 × num_pages × (1 + sum of enabled fractional surcharges),
where js_rendering contributes +1.0 and priority contributes +0.5 to
the per-page multiplier.
js_rendering defaults to false. Pass js_rendering=true to opt into the
+10 credit per-page surcharge in exchange for browser-rendered. This will for instance
make sure images, AI Overview, and hidden elements are displayed.
Examples:
- 1 page, no options → 10 credits (1 base request)
- 5 pages, no options → 50 credits (5 base requests)
- 1 page,
priority=true→ 10 × (1 + 0.5) = 15 credits (1.5 base requests) - 4 pages,
js_rendering=true→ 4 × 10 × (1 + 1) = 80 credits (8 base requests) - 4 pages,
js_rendering=true+priority=true→ 4 × 10 × (1 + 1 + 0.5) = 100 credits (10 base requests)
The POST /keywords response includes a cost object with the exact charge
in credits.
AccuSERP plans are prepaid with a fixed credit balance per billing period
(expressed as base requests on the plan / billing pages — e.g. a plan
advertised as "100,000 base requests" carries 1,000,000 credits). Your balance
resets on each plan renewal. Check GET /status for your current credit
balance, or visit the AccuSERP dashboard for the equivalent
in base requests.
If you have no credits remaining, the API returns 403:
{
"detail": "You have used up all your available credits"
}
Priority queueing
Each request goes to one of two queues:
-
Standard queue (default, when
priority=false). Requests are dispatched in order. During peak load they may wait behind other traffic. -
Priority queue (when
priority=true). Priority requests are dispatched ahead of any standard-queue requests waiting on the same workers. See Credits for the cost surcharge.
When to use which
- The standard queue is the right default for bulk processing, scheduled monitoring, batch imports, and anywhere a wait of a few seconds (or longer during peak load) is acceptable.
- The priority queue is for user-facing real-time lookups, time-sensitive automations, or workflows where a stale result is no longer useful.
Callbacks
When a search completes, AccuSERP POSTs a notification to the URL you
supplied as callback. We recommend callbacks over polling GET /results;
they're cheaper for both sides and not subject to fair-usage throttling.
Best practice: Always provide a
callbackURL. This eliminates the need to poll and ensures you receive results as soon as they're available.
Callback URL requirements
- HTTPS is required in production. HTTP URLs are rejected.
- Allowed ports:
443and80only. Custom ports are rejected. - Public hostnames only. URLs that resolve to private IP ranges (RFC 1918, link-local, loopback) are rejected.
- The body is sent as
application/x-www-form-urlencoded(not JSON).
Delivery behavior
- We make up to 2 delivery attempts: one initial attempt plus one retry on failure. If both attempts fail, the callback is dropped.
-
A failed callback does not affect the result. You can still fetch it via
GET /results?id=<id>until the storage TTL expires. - If the search exceeds its wait window before a worker picks it up, you receive a timeout callback instead (see below) and the credits charged for the request are refunded automatically.
Securing the inbound callback
Callback payloads are not signed. To verify an inbound callback came from AccuSERP, embed a shared secret in the URL when you submit the request:
callback=https://your-server.example.com/accuserp?secret=<your-shared-secret>
Your server then checks for that query parameter on every incoming POST.
The json_callback and html_callback URLs in the payload point at our
/results endpoint. Authenticate those GET calls with your usual
Authorization: Bearer header (recommended) or ?auth_token=<token> query
parameter.
Success payload
| Field | Type | Description |
|---|---|---|
status |
string | "success" or "failed" |
status_code |
integer | HTTP status code of the scrape |
status_description |
string | Human-readable status message |
id |
string | The request ID (same as returned by POST /keywords) |
rank_date |
string | Date (UTC) of the search |
json_callback |
string | URL to retrieve the parsed JSON result (present when save_html is false) |
html_callback |
string | URL to retrieve the raw HTML result (present when save_html is true; points at format=html_per_page when html_per_page is true) |
keyword |
string | The keyword that was searched |
engine |
string | The search engine used |
locale |
string | The locale used |
geo |
string | The geolocation of the search |
mobile |
string | "true" or "false" |
The callback includes either json_callback or html_callback, not both.
Timeout payload
| Field | Type | Description |
|---|---|---|
status |
string | "timeout" |
status_code |
integer | 408 |
status_description |
string | "Request timed out" |
id |
string | The request ID |
Response Format
Metadata
The JSON response from GET /results includes the following top-level metadata
fields alongside the result arrays:
| Field | Type | Description |
|---|---|---|
engine |
string | The search engine used |
locale |
string | The locale used |
keyword |
string | The keyword searched for |
set_location |
string | The geolocation of the search |
rank_date |
string | Date of the search (ISO format) |
requested_at |
string | Time the search was requested (ISO format, UTC) |
mobile |
boolean | Whether the search was performed on a mobile device |
total_number_of_results |
integer | Total results reported by the search engine |
auto_corrected |
boolean | Whether the keyword was auto-corrected by Google |
auto_corrected_to |
string | The auto-corrected keyword (empty string if none) |
related_searches |
array/null | Related search suggestions |
related_questions |
array/null | "People Also Ask" questions |
maps |
boolean | Whether the SERP contains a maps result |
video |
boolean | Whether the SERP contains video results |
serp_count |
integer | Number of organic results |
ad_count |
integer | Number of ad results |
shopping_count |
integer | Number of shopping carousel results |
Organic Results
Organic results are returned in the serp object, keyed by rank position
(1-indexed). Each result contains:
| Field | Type | Description |
|---|---|---|
url |
string | URL of the result |
title |
string | Title of the result |
description |
string | Snippet/description |
base_url |
string | Subdomain + domain |
base_domain |
string | Domain only |
local |
boolean | Whether this is a local result |
video |
boolean | Whether this is a video result |
features |
object | SERP feature flags (keys present = true) |
Possible features: app, breadcrumbs, faq, featured_snippet,
featured_video, forum_post, gmb_cid, hotel_result, https,
image_in_pack, image_in_product_pack, interesting_finds,
knowledge_panel_result, maps_local_teaser, maps_local, news_article,
one_line_site_links, organic_store, popular_store, recipe,
related_questions, research_product, reviews, rich_product_info,
shop_style_item, site_links_links, site_links_searchbox, site_links,
things_to_do_result, thumbnails, tweet, video_in_carousel, video.
Ad & Shopping Results
The ads and shopping objects share the same structure, keyed by position:
| Field | Type | Description |
|---|---|---|
ad_location |
string | "top_ad", "ad_within_organic", or "bottom_ad" |
destination_url |
string | Clean destination URL |
dirty_destination_url |
string | URL with tracking parameters |
display_url |
string | Human-readable display URL |
headline |
string | Ad headline |
description |
string | Ad description |
The ad_location value is determined by the ad's position relative to organic
results on the SERP.
Storage TTLs
Results are stored temporarily and automatically deleted after expiry:
- JSON results: stored for 2 days
- HTML results: stored for 3 hours, also page by page (
html_per_page)
Make sure to retrieve your results before the TTL expires. If you need results for longer, save them to your own storage upon retrieval.