SERP endpoints are for "ask and get an answer" search: you submit a query and the server blocks until done and returns the results directly, unlike scraper's async "submit → poll → fetch" model. Ideal for small jobs; large jobs that exceed the sync window fall back to a pollable run (see below).
Three differences from scraper
| Aspect | scraper (/v1/scraper/*) |
SERP (/v1/serp/*) |
|---|---|---|
| Input | { deliveryMode, recordLimit, targets:[ {...} ], deliveries } (array of targets) |
Flat: query fields at the top level of the body + recordLimit + dataFormat (single query) |
| Return | Async: 201 + run id, poll to fetch |
Synchronous: 200 + results (records array), no polling |
| Push | Supports deliveries webhooks |
Not supported (synchronous return, no async notifications) |
Quick reference
- Base URL:
https://api.antsdata.com/v1 - Method & path:
POST /v1/serp/{platform}/{resource}. Currentlyplatformis onlygoogle; see the endpoint list below forresource. - Auth: header
Authorization: Bearer ants_xxx(static API Key, same as scraper) - Content-Type:
application/json - Request body: query fields flattened at the top level, plus two optional run-level fields:
recordLimit: record cap for this query (omit /0= derived from available credits).dataFormat: result format,json(default, returns a records array) /csv/html/markdown(the latter three return exported text).
Status codes & response body
Uniform envelope { code, message, data }; the shape of data depends on the outcome:
| Case | HTTP | data |
|---|---|---|
Success + json |
200 |
{ "runId", "status":"succeeded", "records":[ … ] } |
Success + csv/html/markdown |
200 |
{ "runId", "status":"succeeded", "format":"csv", "content":"…" } |
| Scrape failed | 200 |
{ "runId", "status":"failed", "run":{ …per-task errorMessage… } } |
| Timed out (not terminal) | 202 |
{ "runId", "status":"queued"\|"running", "run":{ … } } |
- Timeout fallback: if a large query doesn't finish within the server's sync window →
202+runId, and the run keeps running in the background. Switch to polling:GET /v1/serp/runs/{id}, then on successGET /v1/serp/runs/{id}/recordsto fetch orGET /v1/serp/runs/{id}/download?format=json|csv|excelto download. - Payload too large: a synchronous payload > 16 MiB returns an error — use
GET /v1/serp/runs/{id}/downloadto fetch the full result. - Billing: same as scraper, charged per successfully returned record; failures release the hold and are not charged (see Authentication & Errors).
Endpoints
| Endpoint | Path | Description |
|---|---|---|
| Web Search | POST /v1/serp/google/search |
Web / image search results |
| Shopping | POST /v1/serp/google/shopping |
Google Shopping products |
| News | POST /v1/serp/google/news |
Google News articles |
| Maps | POST /v1/serp/google/maps |
Google Maps places / businesses |
| Ad Creatives | POST /v1/serp/google/ads-creatives |
Ads Transparency Center creative list |
| Creative Details | POST /v1/serp/google/ads-creative-details |
Impression / spend / disclosure details for one creative |
Search — Web Search
POST /v1/serp/google/search
Query fields (flattened at the top level of the body):
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | ✅ | Search keyword |
searchType |
string | ✅ | web / image (default web) |
geo_location |
string | ✅ | Geo location, e.g. California,United States |
host_language |
string | ✅ | UI language hl, e.g. en (default en) |
domain |
string | — | Google domain suffix google.<domain> (default com) |
page |
number | — | SERP page number (default 1) |
Output fields (records, one result per record):
| Field | Type | Description |
|---|---|---|
organic |
array | Organic results (searchType=web, one per record) |
organic.pos |
number | Rank within section |
organic.pos_overall |
number | Overall rank on the page |
organic.title |
string | Title |
organic.favicon_text |
string | Site name / favicon text |
organic.url_shown |
string | Breadcrumb / displayed URL |
organic.url |
string | Landing URL |
organic.desc |
string | Snippet |
imageResults |
array | Image results (searchType=image, one per record) |
imageResults.position |
number | Order index |
imageResults.thumbnailUrl |
string | Thumbnail URL |
imageResults.originalUrl |
string | Original image URL |
imageResults.source |
string | Source domain |
imageResults.title |
string | Image title |
total_results_count |
string | Total-results text (e.g. About 1,230,000 results; null for some queries) |
ai_overview |
string | AI overview summary text (null if none) |
Example:
curl -X POST "https://api.antsdata.com/v1/serp/google/search" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "bitcoin",
"searchType": "web",
"geo_location": "California,United States",
"host_language": "en",
"recordLimit": 50
}'
Synchronous response (HTTP 200):
{
"code": 0,
"message": "ok",
"data": {
"runId": "1287345",
"status": "succeeded",
"records": [
{
"organic": {
"pos": 1,
"pos_overall": 1,
"title": "Bitcoin - Open source P2P money",
"url": "https://bitcoin.org/",
"desc": "Bitcoin is an innovative payment network ..."
}
}
]
}
}
Shopping
POST /v1/serp/google/shopping
Query fields:
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | ✅ | Product search term |
gl |
string | ✅ | Country/region (default us) |
hl |
string | ✅ | UI language (default en) |
minPrice |
number | ✅ | Minimum price (local currency) |
maxPrice |
number | ✅ | Maximum price |
condition |
string | — | new / used / refurbished |
sortBy |
string | — | relevance / price_asc / price_desc / rating |
resultsLimit |
number | — | Result cap (default 60; usually driven by recordLimit) |
Output fields (records.products[], one product per record):
| Field | Type | Description |
|---|---|---|
totalResults |
number | Estimated total hits |
products.position |
number | Rank index |
products.productId |
string | Google Shopping product ID |
products.title |
string | Product title |
products.link |
string | Shopping product detail page |
products.price.amount |
string | Current price |
products.price.currency |
string | Currency |
products.price.originalAmount |
string | Struck-through original price |
products.seller |
string | Seller name |
products.rating |
string | Product rating |
products.reviewsCount |
number | Number of reviews |
products.thumbnail |
string | Main thumbnail URL |
products.isSponsored |
boolean | Whether it's a sponsored / ad slot |
products.shipping |
string | Shipping note |
products.badges |
array | Platform badges |
products.offers[].seller |
string | Other seller name |
products.offers[].price |
string | That seller's price |
products.offers[].currency |
string | Currency |
products.offers[].condition |
string | Item condition |
products.offers[].link |
string | Seller landing page |
Example:
curl -X POST "https://api.antsdata.com/v1/serp/google/shopping" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "wireless earbuds",
"gl": "us",
"hl": "en",
"minPrice": 50,
"maxPrice": 300
}'
News
POST /v1/serp/google/news
Query fields:
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | ✅ | Search keyword |
timeRange |
string | ✅ | 1h / 1d / 7d / 1m / 1y (default 7d, keyword only) |
cluster |
boolean | ✅ | Cluster/dedupe same-event stories (default true, keyword only) |
hl |
string | ✅ | Language (default en) |
gl |
string | ✅ | Country/region (default us) |
feedType |
string | — | keyword / top / topic / publication (default keyword) |
topicId |
string | — | Required when feedType=topic |
publicationId |
string | — | Required when feedType=publication |
Output fields (records.articles[], one article per record):
| Field | Type | Description |
|---|---|---|
feedType |
string | Echoes the feed type used |
articles.title |
string | Article title |
articles.link |
string | News link |
articles.source |
string | Publisher name |
articles.published |
string | Published time |
articles.snippet |
string | Snippet |
articles.thumbnail |
string | Thumbnail URL |
articles.clusterSize |
number | Number of related stories (when clustering is on) |
Example:
curl -X POST "https://api.antsdata.com/v1/serp/google/news" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "AI regulation EU",
"timeRange": "7d",
"cluster": true,
"hl": "en",
"gl": "us"
}'
Maps
POST /v1/serp/google/maps
Query fields:
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | ✅ | Address / place / business search term |
location |
string | ✅ | Natural-language area for business search, e.g. Manhattan, NY |
gl |
string | ✅ | Country/region code (default us) |
hl |
string | ✅ | UI language (default en) |
openNow |
boolean | ✅ | Open now only (default false) |
resultsLimit |
number | — | Business-search result cap (default 20; usually driven by recordLimit) |
minRating |
number | — | Minimum-rating filter |
pageToken |
string | — | Pagination token (from previous nextPageToken) |
lat / lng |
number | — | Reverse-lookup coordinates (paired) |
ll |
string | — | Business-search center point, e.g. @40.7128,-74.0060,15z |
mode |
string | — | geocode / reverse / places (default places, usually left blank for auto-detect) |
Output fields (records.places[], one place per record):
| Field | Type | Description |
|---|---|---|
places.placeId |
string | Google Place ID |
places.dataId |
string | Maps data_id |
places.title |
string | Business name |
places.address |
string | Address |
places.rating |
string | User rating |
places.reviewsCount |
number | Total reviews |
places.priceLevel |
string | Price level |
places.categories |
array | Categories |
places.lat |
string | Latitude |
places.lng |
string | Longitude |
places.phone |
string | Phone |
places.website |
string | Website |
places.thumbnail |
string | Cover thumbnail URL |
nextPageToken |
string | Next-page token |
Example:
curl -X POST "https://api.antsdata.com/v1/serp/google/maps" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "coffee shop",
"location": "Manhattan, NY",
"gl": "us",
"hl": "en",
"openNow": false
}'
Ads-creatives — Ad Creatives
POST /v1/serp/google/ads-creatives
Creative list from the Google Ads Transparency Center. The returned
creative_id/sponsor_idcan feed the "Creative Details" endpoint.
Query fields:
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | ✅ | Website domain / keyword, e.g. nike.com (or use advertiser_id) |
regions |
array | ✅ | Region-code array, e.g. ["US","GB"] (default ["us"], region format) |
start_date |
string | ✅ | Start YYYY-MM-DD |
end_date |
string | ✅ | End YYYY-MM-DD |
platform |
number | ✅ | Platform filter 1–6 (1=Search / 2=Display / 3=YouTube / 4=Maps / 5=Play / 6=Shopping) |
advertiser_id |
string | — | Advertiser ID (or use query; comes from suggestions) |
limit |
number | — | Creative cap (1–1000, default 100; capped down by recordLimit) |
topic |
string | — | Set political to switch to the political-ads schema |
Output fields (records, one creative per record):
| Field | Type | Description |
|---|---|---|
creative_id |
string | Creative ID (feeds creative details) |
sponsor_id |
string | Advertiser ID |
format |
string | Creative format (e.g. video) |
regions |
array | Served regions |
first_shown |
string | First shown |
last_shown |
string | Last shown |
Example:
curl -X POST "https://api.antsdata.com/v1/serp/google/ads-creatives" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "nike.com",
"regions": ["US"],
"start_date": "2026-01-01",
"end_date": "2026-06-30",
"platform": 1
}'
Ads-creative-details — Creative Details
POST /v1/serp/google/ads-creative-details
Impression / spend / political-ad disclosure details for a single creative. Get
sponsor_idandcreative_idfrom the "Ad Creatives" endpoint first; don't hardcode them.
Query fields:
| Field | Type | Required | Description |
|---|---|---|---|
sponsor_id |
string | ✅ | Advertiser ID |
creative_id |
string | — | Creative ID (get it from the ad-creatives endpoint first) |
topic |
string | — | Set political to switch to the political schema |
Output fields (records, one creative detail per record):
| Field | Type | Description |
|---|---|---|
creative_id |
string | Creative ID |
sponsor_id |
string | Advertiser ID |
first_shown |
string | First shown (ISO 8601) |
last_shown |
string | Last shown (ISO 8601) |
topic |
string | Ad topic (non-empty when political, else empty string) |
funder |
string | Funder (political-ad disclosure; empty for regular ads) |
regions |
array | Per-region detail, each {code, first_shown, last_shown, min_times, max_times} (impression ranges are strings) |
impressions_lower |
number | Impressions lower bound (political / EU disclosure; null for regular ads) |
impressions_upper |
number | Impressions upper bound (same) |
spend_lower |
number | Spend lower bound (political / EU disclosure; null for regular ads) |
spend_upper |
number | Spend upper bound (same) |
eu_reach |
number | EU reach (EU political-ad disclosure; else null) |
Example:
curl -X POST "https://api.antsdata.com/v1/serp/google/ads-creative-details" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sponsor_id": "AR00040546930815664129",
"creative_id": "CR12886158981977866241"
}'
FAQ
Q: How is SERP different from /v1/scraper/google/*?
They share the same underlying Google scraping capability, but SERP (/v1/serp/google/*) is a synchronous, flat-input wrapper: one call returns results directly, with no targets array and no polling. Use /v1/scraper/google/* for async batches and webhook delivery.
Q: I got a 202 — what now?
202 means the query was large and didn't finish within the sync window, but the run continues in the background. Take the runId and poll GET /v1/serp/runs/{id}; once succeeded, fetch with GET /v1/serp/runs/{id}/records or download via /download.
Q: Which dataFormat values are supported?
json (default, records array) and csv / html / markdown (returns content text + format).
Q: The result is too big to return?
The synchronous payload limit is 16 MiB; larger returns an error. Use GET /v1/serp/runs/{id}/download?format=json|csv|excel to fetch it all at once.
Q: How is it billed?
Same as scraper — charged per successfully returned record; failures release the hold and aren't charged. See Authentication & Errors.
