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
| Endpoint | Returns | Credits |
|---|---|---|
| GET /v1/search | The results page: organic results, AI Overview, knowledge panel, questions | 1 a page, +1 a page read |
| GET /v1/news | The News tab | 1 a page, +1 an article read |
| GET /v1/shopping | The Shopping tab and its filters | 2 a page |
| GET /v1/shopping/product | A product and its stores' offers | 1 |
| GET /v1/images | The Images tab, about 100 a page | 2 a page |
| GET /v1/videos | The Videos tab | 1 a page |
| GET /v1/maps | Places, as Google Maps lists them | 1 a page |
| GET /v1/autocomplete | Search-box suggestions | 1 |
| GET /v1/ai-mode | An AI Mode answer with its sources and places | 5 |
| GET /customsearch/v1 | Google's Custom Search JSON API, drop-in | 1 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.
| Name | Default | Description |
|---|---|---|
| q | — | The query, up to 2,048 bytes. |
| gl | us | Country code: us, gb, de… |
| hl | en | Interface 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. |
| device | desktop | mobile, iphone or ipad for the phone's page. |
| pages | 1 | Pages 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. |
| start | 0 | One 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_cache | false | true asks Google again instead of the hour's cache (see Retries and cache). |
| safe | off | active turns SafeSearch on. |
| lr | — | Result languages: lang_de, up to five joined by |. |
| nfpr | 0 | 1 searches the query as typed, without Google's correction. |
| filter | 1 | 0 keeps similar results. |
| tbs | — | Time range: qdr:h, qdr:d, qdr:w, qdr:m, qdr:y. |
| output | json | md returns Markdown, ready for a language model. |
| html | 0 | 1 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
| Name | Default | Description |
|---|---|---|
| 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
| Field | Description |
|---|---|
| X-Credits | Credits this call spent; meta.credits in the body says the same. |
| X-Cache | hit: the same call by your account in the last hour, answered free. no_cache=true skips it. |
| Idempotent-Replayed | true on an answer given back for an Idempotency-Key. |
| X-Request-Id | The 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
| Tool | Calls |
|---|---|
| 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
| Field | Description |
|---|---|
| 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. |
| webhook | An 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/search
Google's results page, and with content the text of the pages it links to. 1 credit a page, +1 a page read
Parameters
| Name | Default | Description |
|---|---|---|
| common | — | All the common parameters. |
| ai_overview | 0 | 1 adds the AI Overview when the page has one. |
| content | 0 | Also read the first N results' pages, 1 to 10 (true: 10), and give each one's main text as Markdown in organic[].content. A page read is 1 credit; one that could not be read is free. |
| content_chars | 20000 | The most characters of each text, 500 to 100000; a longer one is cut at a paragraph and marked truncated. |
Response
| Field | Description |
|---|---|
| organic[] | The results: position (counted across pages), page, title, url (past Google's redirect), domain, snippet. |
| organic[].content | With content: text (Markdown), title, byline, site_name, published_at, chars, truncated, url when a redirect moved it; or error: timeout, blocked, not_found, not_html, unreadable, unresolved, failed. |
| ai_overview | Its text and sources. |
| related_questions | People also ask: the questions. |
| related_searches | Google's suggestions at the bottom of the page. |
| knowledge_graph | The knowledge panel: title, type, description, facts. |
| top_stories | News on the page: title, url, source, date. |
| showing_results_for | Google's correction of the query. |
curl -G https://api.serpbit.com/v1/search \ -H "Authorization: Bearer $SERPBIT_KEY" \ --data-urlencode "q=running shoes" \ --data-urlencode "ai_overview=1"
{
"request_id": "5f0c2a9e1b7d4c3a8e6f1d20",
"ai_overview": { "text": "Look for cushioning…", "sources": […] },
"organic": [{
"position": 1,
"page": 1,
"title": "The best running shoes of 2026",
"url": "https://www.example.com/running",
"domain": "example.com",
"snippet": "We ran 400 miles in 30 pairs…"
}],
"related_questions": ["How do I choose a running shoe?"],
"pages": [{ "page": 1, "results": 10 }],
"pagination": { "next_start": 10 },
"meta": { "elapsed_ms": 2870 }
}
GET /v1/news
Google's News tab, and with content the articles' text. 1 credit a page, +1 an article read
Parameters
| Name | Default | Description |
|---|---|---|
| common | — | All the common parameters. |
| sort | relevance | date puts the newest first. |
| content, content_chars | 0 | As on /v1/search: the first N articles' text in news[].content, 1 credit an article read. |
Response
| Field | Description |
|---|---|
| news[] | The articles: title, url, source, snippet, thumbnail. |
| date | As Google writes it: 4 hours ago. |
| published_at | When it came out, in UTC. |
| story_id | The 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
| Name | Default | Description |
|---|---|---|
| common | — | All the common parameters. |
| shoprs | — | A filter's token from filters[]; send it with that option's q. |
Response
| Field | Description |
|---|---|
| shopping[] | The products: title, source (the store), price, rating, reviews, delivery, thumbnail. |
| product_id | What /v1/shopping/product takes for all the product's stores. |
| extracted_price | The 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
| Name | Default | Description |
|---|---|---|
| product_id | — | From /v1/shopping. Required, or url. |
| url | — | A Google Shopping link to the product, instead of product_id. |
| more_stores | 3 | Presses of "More stores", 0 to 15; each adds up to ten stores. |
| output | json | md returns Markdown. |
Response
| Field | Description |
|---|---|
| product | title, brand, images. |
| sellers[] | The stores: name, link, price, currency, condition. |
| total_price | The price with delivery_price, when both are known. |
| complete | Whether 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
| Name | Default | Description |
|---|---|---|
| common | — | All the common parameters. |
| size | any | large, medium, icon. |
| color | any | color, gray, transparent, or a color: red, blue, green… |
| type | any | photo, clipart, lineart, animated, face. |
| license | any | creative_commons, commercial. |
| aspect | any | tall, square, wide, panoramic. |
Response
| Field | Description |
|---|---|
| images[] | The pictures: title, url (the page it is on), source. |
| original | The picture itself, with original_width and original_height. |
| thumbnail | Google's copy. |
| product | For 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
| Name | Default | Description |
|---|---|---|
| common | — | All the common parameters. |
| duration | any | short (under 4 min), medium (4 to 20), long. |
Response
| Field | Description |
|---|---|
| videos[] | The videos: title, url, snippet, thumbnail. |
| duration | As Google shows it; duration_seconds in seconds. |
| platform | Where it is: YouTube, Instagram, TikTok…; channel who put it up. |
| video_id | YouTube'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
| Name | Default | Description |
|---|---|---|
| common | — | All the common parameters; place the search with location. |
| start | 0 | On the desktop, 20 is page 2. |
Response
| Field | Description |
|---|---|
| places[] | The places: title, type, rating, reviews, price. |
| address | With hours, phone, website. |
| place_id | Google's ids: also data_id, data_cid, and link to Google Maps. |
| gps_coordinates | latitude 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
| Name | Default | Description |
|---|---|---|
| q | — | The text as typed, spaces kept: how to asks what follows. |
| client | chrome | chrome: 15 suggestions with relevance. gws-wiz: the box's 10, with things Google knows. |
| cp | end of q | Where the cursor is in q. |
| gl | us | Country code. |
| hl | en | Language. |
| output | json | md returns Markdown. |
Response
| Field | Description |
|---|---|
| suggestions[] | position, value, type (QUERY or ENTITY). |
| relevance | Google's score, with client=chrome. |
| title | For 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
| Name | Default | Description |
|---|---|---|
| q | — | The question. |
| gl | us | Country code. |
| hl | en | Language. |
| location | — | A place by Google's name, as in the common parameters. |
| output | json | md returns Markdown. |
Response
| Field | Description |
|---|---|
| text_blocks[] | The answer in Google's order: paragraph, heading, list, code, table. |
| reference_indexes | A 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. |
| markdown | The 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
| Name | Default | Description |
|---|---|---|
| key | — | Your Serpbit key; a header works too. |
| q | — | The query. |
| num | 10 | Results, 1 to 10. |
| start | 1 | The 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
| Field | Description |
|---|---|
| items[] | As Custom Search: title, link, displayLink, snippet and their html… forms; no pagemap. |
| queries | request and nextPage. |
| totalResults | The last result known, plus 10 when there is more: not Google's estimate. |
| error | In 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.
| Status | error | What to do |
|---|---|---|
| 400 | bad_request | Fix the parameter the message names. |
| 401 | unauthorized | Check the key. |
| 402 | out_of_credits | Top up or upgrade in the dashboard. |
| 404 | product_not_found | Google has no such product. |
| 429 | rate_limited | Wait the seconds in Retry-After. |
| 503 | no_results_page, no_answer, unavailable | Google 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.
| Plan | Credits / mo | Req / min | Concurrent |
|---|---|---|---|
| Free | 1,000 | 30 | 2 |
| Starter | 25,000 | 120 | 5 |
| Developer | 100,000 | 300 | 10 |
| Production | 300,000 | 600 | 20 |
| Scale | 800,000 | 1,200 | 40 |