API reference

Serpbit API

Google's results as JSON or Markdown: the results page and its tabs, products, suggestions and AI Mode. Only answers are billed.

Quick start

Create a key and send it in a header: Authorization: Bearer … or X-API-Key. A missing or revoked key gets 401. Keep keys out of browser code.

curl -G https://api.serpbit.com/v1/search \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=coffee near me"

Endpoints

EndpointReturnsCredits
GET /v1/searchThe results page: organic results, AI Overview, knowledge panel, questions1 a page, +1 a page read
GET /v1/newsThe News tab1 a page, +1 an article read
GET /v1/shoppingThe Shopping tab and its filters2 a page
GET /v1/shopping/productA product and its stores' offers1
GET /v1/imagesThe Images tab, about 100 a page2 a page
GET /v1/videosThe Videos tab1 a page
GET /v1/mapsPlaces, as Google Maps lists them1 a page
GET /v1/autocompleteSearch-box suggestions1
GET /v1/ai-modeAn AI Mode answer with its sources and places5
GET /customsearch/v1Google's Custom Search JSON API, drop-in1 a page, 2 for pictures

All parameters go in the query string. Only answers cost credits; failed calls are free. Every answer says what it spent in meta.credits.

Common parameters

Search, News, Shopping, Images, Videos and Maps take these. Only q is required.

NameDefaultDescription
q—The query, up to 2,048 bytes.
glusCountry code: us, gb, de…
hlenInterface language: en, de, ja…
location—A place by Google's name, down to a ZIP code: Austin,Texas,United States. Or uule, Google's encoding.
devicedesktopmobile, iphone or ipad for the phone's page.
pages1Pages 1 to N in one call, up to 10 (100 results). Each page is billed. The results come as one list, numbered across the pages, each with its page and a result Google repeats on a later page shown once; pages[] says how many each page gave.
num—Results wanted, up to 100, for Search and News: Google gives ten a page since September 2025, so num=30 is pages 1-3. Not with pages.
start0One later page: 10 is page 2, up to 190.
url—/v1/search only: a Google results address as a browser shows it, read into these parameters and its tab (News, Shopping, Images, Videos, Maps). Not with the parameters it sets; device, pages, output, html and location go beside it. What it carried and was not read comes back in search.url_ignored.
no_cachefalsetrue asks Google again instead of the hour's cache (see Retries and cache).
safeoffactive turns SafeSearch on.
lr—Result languages: lang_de, up to five joined by |.
nfpr01 searches the query as typed, without Google's correction.
filter10 keeps similar results.
tbs—Time range: qdr:h, qdr:d, qdr:w, qdr:m, qdr:y.
outputjsonmd returns Markdown, ready for a language model.
html01 adds each page's HTML as pages[].html.

Page on until pagination.next_start is null. If a call for several pages returns fewer and next_start is not null, a page timed out: ask again from there. Ten pages take up to a minute: set a 90-second timeout.

Retries and cache

Retry safely, and never pay twice for the same answer.

Request headers

NameDefaultDescription
Idempotency-Key—Any 1-255 characters, a UUID for example. The same key again within 24 hours gets the first answer back, free. With other parameters it is 422 idempotency_key_reused. A repeat while the first call runs waits for it. A failed call is not kept, so its retry runs again.

Response headers

FieldDescription
X-CreditsCredits this call spent; meta.credits in the body says the same.
X-Cachehit: the same call by your account in the last hour, answered free. no_cache=true skips it.
Idempotent-Replayedtrue on an answer given back for an Idempotency-Key.
X-Request-IdThe call's id, also request_id: quote it to support.
curl -G https://api.serpbit.com/v1/search \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  -H "Idempotency-Key: 7c0e7a52-3f6b-4e2b-9a61-5d2f0c8e1b44" \
  --data-urlencode "q=running shoes"
HTTP/2 200
x-credits: 0
idempotent-replayed: true
x-request-id: 5f0c2a9e1b7d4c3a8e6f1d20

MCP server

Give an AI agent Google as tools: https://api.serpbit.com/mcp, Streamable HTTP. Each tool is the API call of the same name, at the same price. Answers come as Markdown, or as JSON with format: "json".

Tools

ToolCalls
google_search/v1/search
google_news/v1/news
google_shopping, google_shopping_product/v1/shopping, /v1/shopping/product
google_images, google_videos/v1/images, /v1/videos
google_maps, google_autocomplete/v1/maps, /v1/autocomplete
google_ai_mode/v1/ai-mode

Connect

Sign in, no key. In Claude, ChatGPT, Cursor or VS Code, add a custom connector with the URL alone. The app opens a Serpbit sign-in, and once you allow it, it gets a token of its own (OAuth 2.1 with PKCE). Each app is listed under Connected apps, and Disconnect there stops it at once.

Or a key. An agent with no browser sends a key as Authorization: Bearer. A client that takes only a URL can use https://api.serpbit.com/mcp?key=…, but keep that URL private.

?tools=google_search,google_news offers only those tools.

# Claude, ChatGPT: Settings → Connectors → Add custom connector
https://api.serpbit.com/mcp
# Claude Code: add it, then sign in with /mcp
claude mcp add --transport http serpbit https://api.serpbit.com/mcp

# or with a key
claude mcp add --transport http serpbit https://api.serpbit.com/mcp \
  --header "Authorization: Bearer $SERPBIT_KEY"
// Cursor: ~/.cursor/mcp.json · VS Code: .vscode/mcp.json ("servers", "type": "http")
{
  "mcpServers": {
    "serpbit": { "url": "https://api.serpbit.com/mcp" }
  }
}

POST /v1/batches

Up to 1,000 calls in one request, run in the background at the key's own limits. Each call is the API's own, billed as it would be alone. each call's credits

Body

FieldDescription
requests[]The calls: path (/v1/search, /v1/news, /v1/shopping, /v1/shopping/product, /v1/images, /v1/videos, /v1/maps, /v1/autocomplete, /v1/ai-mode), params as that endpoint takes them (no html), and an id of yours.
webhookAn https address that gets each answer as it comes, then the batch's end.

The answer is the batch: its id, status (running, completed), counts and credits. GET /v1/batches/{id} shows each call's status; GET /v1/batches/{id}/results?offset=0&limit=100 gives the answers. They are kept in memory for 24 hours after the batch ends, never on disk. A batch shares its key's limits with the key's other calls: give batches a key of their own. An Idempotency-Key on the POST gives the same batch back.

Webhooks

Each event is a POST of {"type", "data"}: batch.item with a call's index, id, status, credits and answer; batch.completed with the batch. An event that does not get a 2xx is sent twice more, and one may now and then arrive twice: tell events apart by batch_id and index. Check Serpbit-Signature: t=<unix>,v1=<hex>: the HMAC-SHA256 of t + "." + body with your API key. The Python and JavaScript clients have verify_webhook / verifyWebhook.

curl https://api.serpbit.com/v1/batches \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"webhook": "https://example.com/serpbit",
       "requests": [
         {"path": "/v1/search", "params": {"q": "running shoes", "gl": "de"}, "id": "kw-1"},
         {"path": "/v1/search", "params": {"q": "trail shoes", "pages": 2}, "id": "kw-2"}
       ]}'
{
  "id": "bat_5f0c2a9e1b7d4c3a8e6f1d20",
  "status": "running",
  "total": 2, "done": 0, "succeeded": 0, "failed": 0,
  "credits": 0,
  "created_at": "2026-11-08T21:04:05Z"
}

Python and JavaScript

Small clients with no dependencies: a method for every endpoint, its parameters as the tables below name them. They retry 429 and 5xx with one Idempotency-Key per call, so an answer lost on the way is never billed twice, and raise an error class for each refusal (InsufficientCreditsError, RateLimitError…) with its request_id.

Both read the key from SERPBIT_API_KEY. JavaScript runs on Node 18+, Deno, Bun and in browsers, with TypeScript types; keep the key on a server.

The packages are published on PyPI and npm with the public launch.

# pip install serpbit
from serpbit import Serpbit

sb = Serpbit()
answer = sb.search("espresso grinder", gl="de", content=3)
print(answer["organic"][0]["content"]["text"])
// npm install serpbit
import Serpbit from "serpbit";

const sb = new Serpbit();
const answer = await sb.search("espresso grinder", { gl: "de", content: 3 });
console.log(answer.organic[0].content?.text);

GET /v1/news

Google's News tab, and with content the articles' text. 1 credit a page, +1 an article read

Parameters

NameDefaultDescription
common—All the common parameters.
sortrelevancedate puts the newest first.
content, content_chars0As on /v1/search: the first N articles' text in news[].content, 1 credit an article read.

Response

FieldDescription
news[]The articles: title, url, source, snippet, thumbnail.
dateAs Google writes it: 4 hours ago.
published_atWhen it came out, in UTC.
story_idThe same on every article of one story.
curl -G https://api.serpbit.com/v1/news \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=wildfire" \
  --data-urlencode "sort=date"
{
  "news": [{
    "position": 1, "page": 1,
    "title": "Crews work to contain wildfire in the White Mountains",
    "url": "https://www.example.com/news/wildfire",
    "source": "WGME",
    "date": "20 hours ago",
    "published_at": "2026-11-05T22:47:47Z",
    "story_id": "4906325574"
  }],
  "pages": [{ "page": 1, "results": 10 }]
}

GET /v1/shopping

Google's Shopping tab, without sponsored products. 2 credits a page

Parameters

NameDefaultDescription
common—All the common parameters.
shoprs—A filter's token from filters[]; send it with that option's q.

Response

FieldDescription
shopping[]The products: title, source (the store), price, rating, reviews, delivery, thumbnail.
product_idWhat /v1/shopping/product takes for all the product's stores.
extracted_priceThe price as a number; old_price when it is on sale.
filters[]The tab's filters by group (Price, Brand…), each option with text, q, shoprs.
curl -G https://api.serpbit.com/v1/shopping \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=running shoes"
{
  "shopping": [{
    "position": 1, "page": 1,
    "title": "Brooks Women's Glycerin Max 2",
    "product_id": "10327006607387456741",
    "source": "Fitness Sports",
    "price": "$149.95", "extracted_price": 149.95,
    "rating": 4.4, "reviews": 407
  }],
  "filters": [{ "type": "Price", "options": [
    { "text": "Under $100", "q": "running shoes under $100", "shoprs": "CAESFhIU…" }
  ] }]
}

GET /v1/shopping/product

A product and the stores that sell it, for US buyers in English. An unknown product is 404 and free. 1 credit a product

Parameters

NameDefaultDescription
product_id—From /v1/shopping. Required, or url.
url—A Google Shopping link to the product, instead of product_id.
more_stores3Presses of "More stores", 0 to 15; each adds up to ten stores.
outputjsonmd returns Markdown.

Response

FieldDescription
producttitle, brand, images.
sellers[]The stores: name, link, price, currency, condition.
total_priceThe price with delivery_price, when both are known.
completeWhether sellers is the whole list.
curl -G https://api.serpbit.com/v1/shopping/product \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "product_id=10573081257954387607"
{
  "product": { "title": "Timemore C2 Coffee Grinder", "brand": "Time More" },
  "sellers": [{
    "name": "mercari.com",
    "price": 46.66, "currency": "USD",
    "delivery_price": 0, "total_price": 46.66,
    "link": "https://www.mercari.com/us/item/…"
  }],
  "sellers_count": 11,
  "complete": true
}

GET /v1/images

Google's Images tab: about 100 pictures a page, 50 on a phone. 2 credits a page

Parameters

NameDefaultDescription
common—All the common parameters.
sizeanylarge, medium, icon.
coloranycolor, gray, transparent, or a color: red, blue, green…
typeanyphoto, clipart, lineart, animated, face.
licenseanycreative_commons, commercial.
aspectanytall, square, wide, panoramic.

Response

FieldDescription
images[]The pictures: title, url (the page it is on), source.
originalThe picture itself, with original_width and original_height.
thumbnailGoogle's copy.
productFor a picture sold as a product: its store, price and product_id.
curl -G https://api.serpbit.com/v1/images \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=eiffel tower" \
  --data-urlencode "size=large"
{
  "images": [{
    "position": 1, "page": 1,
    "title": "Eiffel Tower - Wikipedia",
    "url": "https://en.wikipedia.org/wiki/Eiffel_Tower",
    "original": "https://upload.wikimedia.org/…/Tour_Eiffel.jpg",
    "original_width": 330, "original_height": 550,
    "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:…"
  }],
  "pages": [{ "page": 1, "results": 100 }]
}

GET /v1/videos

Google's Videos tab, ten a page. 1 credit a page

Parameters

NameDefaultDescription
common—All the common parameters.
durationanyshort (under 4 min), medium (4 to 20), long.

Response

FieldDescription
videos[]The videos: title, url, snippet, thumbnail.
durationAs Google shows it; duration_seconds in seconds.
platformWhere it is: YouTube, Instagram, TikTok…; channel who put it up.
video_idYouTube's id of the video.
curl -G https://api.serpbit.com/v1/videos \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=eiffel tower" \
  --data-urlencode "duration=short"
{
  "videos": [{
    "position": 1, "page": 1,
    "title": "Gustave Eiffel, The Eiffel Tower",
    "url": "https://www.youtube.com/watch?v=vurh8_SO6_c",
    "duration": "9:26", "duration_seconds": 566,
    "platform": "YouTube", "channel": "Smarthistory",
    "video_id": "vurh8_SO6_c"
  }],
  "pages": [{ "page": 1, "results": 10 }]
}

GET /v1/maps

Places as Google Maps lists them: 20 a page on the desktop, 10 on a phone. 1 credit a page

Parameters

NameDefaultDescription
common—All the common parameters; place the search with location.
start0On the desktop, 20 is page 2.

Response

FieldDescription
places[]The places: title, type, rating, reviews, price.
addressWith hours, phone, website.
place_idGoogle's ids: also data_id, data_cid, and link to Google Maps.
gps_coordinateslatitude and longitude, on the desktop.
curl -G https://api.serpbit.com/v1/maps \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=coffee shops" \
  --data-urlencode "location=Philadelphia,Pennsylvania,United States"
{
  "places": [{
    "position": 1, "page": 1,
    "title": "Freedom Coffee House",
    "type": "Coffee shop",
    "rating": 5, "reviews": 66,
    "address": "4906 Old York Rd, Philadelphia, PA 19141",
    "phone": "(267) 441-2131",
    "place_id": "ChIJG9bzoEu3xokR6Mly5crxWPs"
  }],
  "pages": [{ "page": 1, "results": 20 }],
  "pagination": { "next_start": 20 }
}

GET /v1/autocomplete

Google's search-box suggestions for text as typed. 1 credit a call

Parameters

NameDefaultDescription
q—The text as typed, spaces kept: how to asks what follows.
clientchromechrome: 15 suggestions with relevance. gws-wiz: the box's 10, with things Google knows.
cpend of qWhere the cursor is in q.
glusCountry code.
hlenLanguage.
outputjsonmd returns Markdown.

Response

FieldDescription
suggestions[]position, value, type (QUERY or ENTITY).
relevanceGoogle's score, with client=chrome.
titleFor a thing Google knows, with description and thumbnail (gws-wiz).
curl -G https://api.serpbit.com/v1/autocomplete \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=golden ret" \
  --data-urlencode "client=gws-wiz"
{
  "suggestions": [
    { "position": 1, "value": "golden retriever", "type": "ENTITY",
      "title": "Golden Retriever", "description": "Dog breed" },
    { "position": 2, "value": "golden retriever puppies", "type": "QUERY" }
  ]
}

GET /v1/ai-mode

Google's AI Mode answer. Usually 5 to 20 seconds, up to a minute: set a 120-second timeout. A call that gets no answer is free. 5 credits an answer

Parameters

NameDefaultDescription
q—The question.
glusCountry code.
hlenLanguage.
location—A place by Google's name, as in the common parameters.
outputjsonmd returns Markdown.

Response

FieldDescription
text_blocks[]The answer in Google's order: paragraph, heading, list, code, table.
reference_indexesA block's citations, as indexes into references.
references[]The sources: title, link, snippet, source.
local_results[]Places the answer names: rating, address, phone, Google Maps ids.
markdownThe whole answer as Markdown.
curl -G https://api.serpbit.com/v1/ai-mode \
  -H "Authorization: Bearer $SERPBIT_KEY" \
  --data-urlencode "q=why is the sky blue" \
  --max-time 120
{
  "text_blocks": [
    { "type": "paragraph", "snippet": "The sky is blue because of Rayleigh scattering…",
      "reference_indexes": [0, 1] },
    { "type": "heading", "snippet": "How It Works", "level": 4 }
  ],
  "references": [
    { "index": 0, "title": "Why Is the Sky Blue? | NASA Space Place",
      "link": "https://spaceplace.nasa.gov/blue-sky/", "source": "NASA Space Place (.gov)" }
  ],
  "markdown": "The sky is blue because of Rayleigh scattering…"
}

GET /customsearch/v1

Google's Custom Search JSON API stops searching the whole web on January 1, 2027. Serpbit takes the same request and answers the same JSON: change the host to https://api.serpbit.com and key to your Serpbit key. 1 credit a Google page read, 2 for pictures

Parameters

NameDefaultDescription
key—Your Serpbit key; a header works too.
q—The query.
num10Results, 1 to 10.
start1The first result, up to 100 in all. A window across two of Google's pages (start=5) reads two.
gl, hl, lr, safe, filter—As Custom Search has them.
dateRestrict—d7, w2, m1, y1.
exactTerms, excludeTerms, orTerms, hq—Written into the query as Google's operators.
siteSearch, fileType—One site (or not, siteSearchFilter=e); one file type: pdf.
searchType—image for pictures, with imgSize, imgType, imgColorType, imgDominantColor.
cx—Accepted and not needed: the results are the whole web's.
cr, sort, linkSite, rights, lowRange, highRange—Not supported: answered 400.

Response

FieldDescription
items[]As Custom Search: title, link, displayLink, snippet and their html… forms; no pagemap.
queriesrequest and nextPage.
totalResultsThe last result known, plus 10 when there is more: not Google's estimate.
errorIn Custom Search's shape; out of credits is 429 quotaExceeded.
curl "https://api.serpbit.com/customsearch/v1?key=$SERPBIT_KEY&q=espresso+grinder&num=10"
{
  "kind": "customsearch#search",
  "queries": { "nextPage": [{ "startIndex": 11, "count": 10 }] },
  "items": [{
    "title": "The 6 Best Espresso Grinders of 2026",
    "link": "https://www.example.com/best-espresso-grinders",
    "displayLink": "www.example.com",
    "snippet": "We tested 25 grinders…"
  }]
}

Errors

JSON, such as {"error": "rate_limited", "message": "…", "request_id": "…"}, and never billed. Quote the request_id to support.

StatuserrorWhat to do
400bad_requestFix the parameter the message names.
401unauthorizedCheck the key.
402out_of_creditsTop up or upgrade in the dashboard.
404product_not_foundGoogle has no such product.
429rate_limitedWait the seconds in Retry-After.
503no_results_page, no_answer, unavailableGoogle gave nothing in time. Retry.

Limits and credits

Limits are per key. Plan credits reset on renewal (the free plan on the 1st, UTC); top-ups never expire and are spent after the plan's.

PlanCredits / moReq / minConcurrent
Free1,000302
Starter25,0001205
Developer100,00030010
Production300,00060020
Scale800,0001,20040