Serper API Key and Docs: From Login to Your First Result
Serper has no docs site: the Playground is the docs, and keys can only be created, renamed or reset. 34 live calls map params, errors and key rotation.

Most people who search for "serper api key", "serper api docs" or "serper api login" hit the same wall. Serper has no separate documentation site. On November 5, 2026, serper.dev/docs returned a 404. The working "docs" are the Playground, a page you can only use after you log in. The parameter rules, the credit costs and the sample code all live in that page's JavaScript.
We read the public serper.dev pages and their front-end scripts. Then we sent 34 live requests to the EveryInfra search API between 00:18 and 00:54 SGT on the same day. Twenty-one requests returned results and cost $0.015269 in total. The other 13 were deliberate mistakes, a wrong key header, rate-limit hits and one empty result, and all of them cost $0.
This guide does not compare prices. It compares the path to a working call. How many steps lead from sign-up to the first result? Where does the key live, how many keys can you create, and what do you do after a leak?
It maps q, gl, hl, num, page and type to our parameters. It shows the real error bodies. It also shows where to put the key in Python, n8n and MCP clients. For pricing, read our three earlier Serper posts, linked in the first section.

What this guide covers, and where to find pricing
The price summary in one line: Serper's four packs cost $0.30 to $1.00 per 1,000 credits and expire after 6 months. EveryInfra web search costs $0.000694 per call ($0.694 per 1,000) with no expiry. For cost per 1,000 queries, read Serper API Alternative: Pricing, Free Tier, Real Tests. For how long 2,500 free queries last, read Serper API Free Tier. For the four packs month by month, read Serper API Pricing Explained.
This post is for a developer who uses Serper or plans to try it. It covers setup and debugging only. You will see where to get the key and how to send it. You will also see where the parameter rules are, what to do when a call fails, and how to replace a leaked key.
For Serper we used public material only. That means the home page, sign-up page, login page, terms, privacy policy and the JavaScript those pages load into any visitor's browser. We did not create a Serper account, use a Serper key or call the Serper search API. So every statement about "what you see after login" comes from text in those scripts or from public third-party posts, and we name the source each time. Every EveryInfra number comes from a live response on November 5, 2026.
The comparison table: key, docs and first request
The table holds only facts you can check. The Serper column comes from serper.dev pages and scripts, read on November 5, 2026. The EveryInfra column comes from the docs, the pricing page, the OpenAPI file, the public catalog and our live calls. We read the docs, pricing page and OpenAPI file again at 01:25 SGT on November 5, 2026 and kept copies.
| Item | Serper | EveryInfra |
|---|---|---|
| Sign-up | Name, email, password, plus a human check; email verification after sign-up | Email, or continue with GitHub or Google |
| Login | Email and password; optional 2FA with an authenticator app | Same as sign-up |
| Free usage | 2,500 queries, no credit card | Star two GitHub repos for a $1 trial credit, valid for 24 hours |
| Where the key is | API Keys page in the dashboard | API Keys in the console; the full key shows once, at creation |
| Keys per account | Several, each with a name | Several, split by environment or use |
| Key management | Create, rename, copy, reset | Create, scope by product, disable, IP allowlist, rotate |
| Auth header | X-API-KEY: <key> (the GET variant puts apiKey in the URL) | Authorization: Bearer <key> |
| Endpoint | https://google.serper.dev/<type> | https://api.everyinfra.com/api/v1/search |
| Where the docs are | Playground (after login); no docs site | /docs, /llms.txt, /openapi.json and a free tool catalog |
| Machine-readable spec | No OpenAPI or llms.txt found | OpenAPI 3.0.3 with 61 paths, llms.txt, tool catalog JSON |
| MCP | None from Serper; community servers that run locally | Remote MCP server at https://api.everyinfra.com/mcp |
| Error shape | {"message": "...", "statusCode": 403} (third-party posts) | {"error": {"code", "message", "fault", "retryable", "next_steps", ...}} |
Three rows matter most. The first is "Where the docs are". Serper puts parameters, sample code and credit costs inside an interactive page that needs a login. That works well for a person, but an AI coding assistant needs an extra step to read it. The second is "Key management".
The Serper key page has four actions and no delete. You cannot limit what one key can do or where it can be used from. The third is the auth header. The two APIs use different headers, and this is the change people forget most often. In our test, a valid key sent in X-API-KEY returned a 401.
Serper API login: the sign-up and login steps
Serper's sign-up page says "Create an account" and "Create a free account, no credit-card needed." The form has three required items: name (first and last), email and password (serper.dev/signup, read on November 5, 2026). A human check sits under the form, and the "Create account" button only works after you pass it. The page script sends two human-check tokens with the request. After sign-up, a page says "Please verify your email account", with options to resend the email or log out. A good link shows "Your email has been verified." An old link shows "The verification link is invalid or has expired."

The login page says "Log in to your account". It has email, password, "Remember me" and "Forgot password?" (serper.dev/login). If you turn on two-factor login in Settings, you also type a code from an authenticator app. The settings text reads "Scan the QR code below with your authenticator app". We found no Google or GitHub login in the sign-up or login scripts.
So "serper api login" has one path: email and password. One small detail explains a lot of search results. If you open serper.dev/api-keys while logged out, the page shows the login form. That is why the top result for "serper api login" often has the title "Log in to your account" but the URL /api-keys.
Serper's terms of service say "Updated on May 29, 2024". Two rules matter for accounts. The first forbids users to "Register more than one account at any point in time". So extra accounts for extra free queries break the terms.
The second forbids any "Attempt to circumvent any restrictions or limits placed on your Serper API key or account". The terms also say that Serper is a business-to-business service. The privacy policy shows "Date Last Reviewed: 5 November 2025" and says that Serper collects access logs and activity logs.
The EveryInfra sign-up starts from "Start free" at the top right of the site. The page has one email field and an optional referral code, then "Continue with GitHub" and "Continue with Google". We did not create a new account either. So the steps after sign-up come from the page and the docs. The quickstart says you create an API key in the console after sign-up, and that the full key shows only once.

The main setup difference is one step. After Serper sign-up, you open a verification link in your inbox and then log in with email and password. On EveryInfra you can continue with GitHub or Google and skip the inbox. The $1 EveryInfra trial adds a trip to GitHub to star two repos, but that step is for credit, not for the key. For an AI assistant that runs the setup, each inbox step needs a human and stops the flow.
Where is the Serper API key, and what can you do with it?
The Serper key lives on the API Keys page in the dashboard. The page title is "API Keys" and the subtitle is "Your keys to unlock the power of Serper". The page script calls three account endpoints: list keys, create a key and rename a key. A key must have a name, and the form says "Name is required" if you leave it blank.
Each key also has a "Reset" action. The reset dialog reads "Are you sure you want to reset this API key? This action cannot be undone and any applications using this key will stop working." After a reset, the page says "The old API key is not valid anymore. Replace it with the new one."
The page also has a copy button with the message "API key copied to clipboard". So the full key stays visible in the dashboard, and anyone with your login can copy it again later. The script has no endpoint to delete a key. It has no setting to limit one key to some products, to a spend cap or to some source IP addresses.
The Settings page covers your name, password, two-factor login and payment card. An "Activity logs" page lists recent requests with the columns Time, Query, Type, Page, Num, Credits and Response time (ms). You can filter it by key. An "Anonymize queries" switch stops the log from keeping your query text.
| Action | Serper (API Keys page script) | EveryInfra (docs, pricing page, OpenAPI) |
|---|---|---|
| Create | Yes, a name is required | Yes, one per environment or use |
| Rename | Yes | Yes (PATCH /portal/api/keys/{key_id} in OpenAPI) |
| See the full key again | Copy button on the page | Full key shows once, at creation |
| Reset or rotate | Yes, the old key dies at once | Yes, in the console only |
| Disable but keep | Not found | Yes, calls then return 401 |
| Delete | Not found | Console keys can only be disabled; keys made through the API can be deleted |
| Scope by product | Not found | Yes, a blocked product returns 403 capability_denied |
| IP allowlist | Not found | Yes, in the console only |
| Logs per key | Yes, Activity logs filter by key | Yes, console call history; OpenAPI has a per-key usage endpoint |
| Spend limit and expiry per key | Not found | Yes, spend_limit_usd and expires_at (OpenAPI) |
A missing feature does not make Serper unusable. One developer with one key for one script needs only create and reset. The gap shows up with teams, several environments and AI agents. Suppose one key sits on your laptop, in CI and in a demo notebook you shared with a client.
If the notebook leaks, a Serper reset breaks all three places at once. With separate keys per environment, you disable only the leaked one. The EveryInfra docs use a fuse-box picture for this. One key is the main switch, and API Keys → Permission limits turns off single circuits (quickstart, auth section).
Managing keys from a script: the key endpoints in the EveryInfra OpenAPI file
The console is not the only way to manage keys. The Account section of the EveryInfra OpenAPI file lists a set of key endpoints, which we read at 01:25 SGT on November 5, 2026. None of them is charged. They share a limit of 60 requests per minute per account with the other account endpoints.
Read endpoints need account read scopes. A key with full product access has account:read, requests:read and billing:read by default. A key limited to some products gets them only after the account owner turns them on in the console. Write endpoints need the keys:write scope. That scope is off by default, and only the account owner can grant it, signed in to the console. Each change made with a key shows up in the console notifications.
| Endpoint | What it does | Limits in the spec |
|---|---|---|
GET /portal/api/keys | Lists each key's name, masked prefix, status, scopes, per-minute limit, USD spend limit and last use | Never returns the key secret |
POST /portal/api/keys | Creates a key and returns it once; can set ip_allow, expires_at and spend_limit_usd | A key made through the API is never broader than the calling key and never gets a write scope; at most 20 per account per UTC day; a key with a spend limit cannot create keys |
PATCH /portal/api/keys/{key_id} | Renames a key, changes its note or moves its expiry earlier | Only for keys made through the API or the calling key; console keys can only be disabled |
POST /portal/api/keys/{key_id}/disable | Disables any key on the account at once | Only the account owner can enable it again, in the console |
DELETE /portal/api/keys/{key_id} | Deletes a key for good | Only for keys made through the API or the calling key |
GET /portal/api/keys/{key_id}/usage | Shows one key's successes, failures, spend and last call over recent days | Needs requests:read |
These fields matter after a leak. spend_limit_usd is a lifetime USD cap for one key: charges minus refunds, and it never resets. Give the CI key or the demo notebook key a small cap, and a leak has a ceiling. expires_at makes a temporary key stop on its own. ip_allow makes a key work only from your own server's IP addresses.
We found no matching options in the Serper API Keys page script. This section follows the OpenAPI text. We did not create test keys to call each endpoint.
Serper API documentation: the Playground is the docs
First we checked for a separate docs site. On November 5, 2026, serper.dev/docs, serper.dev/llms.txt, serper.dev/openapi.json and serper.dev/sitemap.xml all returned 404. The "Product" column in the home page footer lists the Playground and Dashboard, plus integration links for Haystack, Jan AI, CrewAI and LangChain. It has no "Docs" or "API reference" link.
Serper's GitHub organization, Serper-API, has 7 repositories. GitHub marks all of them as forks of other projects, such as Auto-GPT, chat-ui and Perplexica. None is an SDK or an API spec.
So "serper api documentation" in practice means the Playground. It is a form: you pick a result type and type a query on the left, and you get results and code on the right. We read its script and listed seven facts that matter when you write code against it.
First, the result type is part of the URL, not the request body. The script adds the chosen type to https://google.serper.dev/<type> and removes type from the body before it sends. The menu holds search, images, videos, places, news, shopping, scholar, patents, autocomplete, product-reviews, lens, maps and reviews, and some types depend on the account. Web page scraping uses another host, https://scrape.serper.dev.
A "Resolve links" tool costs 0 credits. Second, parameters have defaults, and the script drops any parameter that equals its default. The defaults are gl=us, hl=en, page=1, num=10 and autocorrect=true.
Third, the result count menu for plain search now has one choice, 10. Images offer 10 or 100, and shopping uses 40. Fourth, a "Mini-batch (up to 100 queries)" switch puts a list of queries into one request. Fifth, credits. A live "Credits: N" widget shows the cost of the current request.
The script sets 3 credits for maps and lens, 2 for shopping and product-reviews, and 1 for most other types. Web page scraping costs 2 credits, or 6 or 10 for hard pages. In the script's words, "Most webpages typically incur 2 credits, but for harder-to-scrape pages ... the cost can be 6 or 10 credits". Images over 10 results cost double.
Sixth, sample code. The code panel uses Postman's open-source postman-code-generators library. The language menu comes straight from its getLanguageList() call, with Node.js as the default. You can pick POST or GET.
POST sends the key in an X-API-KEY header. GET puts every parameter and the apiKey into the URL. A key in a URL ends up in server access logs, browser history and proxy logs. So use POST unless your tool only speaks GET.
An n8n community post from July 26, 2025 shows this trap. A user sent GET with a header and got a 403. The reply explained that GET needs the key in the URL, and that POST is the method that reads the header (n8n community thread).
Seventh, locations. The location field says "You can find a list of supported locations here" and links to api.serper.dev/locations. We downloaded that public list. It has 100,029 entries in 240 countries and territories, including 48,384 postal codes, 37,208 cities and 4,309 neighborhoods. That is far finer than the EveryInfra location field. Our tool catalog says to use "a country name or two-letter country code; a city counts as its country".
| Playground rule | Where it is | What it means for your code |
|---|---|---|
| Type is in the URL, not the body | https://google.serper.dev/ + type | Change the URL to change the type |
| Defaults gl=us, hl=en, num=10, page=1 | Removed from generated code | No parameters means US English |
| Plain search num has one choice, 10 | Result count menu | Use page for more results |
| Credit cost differs by type | "Credits: N" widget | maps and lens cost 3 credits |
| Up to 100 queries per request | Mini-batch switch | Credits count per query |
| GET puts apiKey in the URL | GET/POST switch in the code panel | Use POST and the header |
| 100,029 locations | api.serper.dev/locations | Down to city and postal code |
Docs that live inside an interactive page have a cost, and one search measured it. At 00:18:40 and 00:19:06 SGT we ran "serper api documentation" through EveryInfra web search with location: us and took the first two pages, 20 results. Only 3 URLs were on serper.dev: the home page, /api-keys (a login form when logged out) and /signup. None was a docs page.
Eight results were docs from other projects: CrewAI, LangChain (three, one an old mirror), Langflow, Haystack, Sim and Aikeedo. Four were API directories. The last five were the GitHub organization, a Medium post, a YouTube video, an MCP listing and another search API's home page.
| Source of the top 20 results | Count | Examples |
|---|---|---|
| serper.dev pages | 3 | Home, /api-keys, /signup |
| Framework and platform docs | 8 | docs.crewai.com, reference.langchain.com, docs.haystack.deepset.ai, docs.langflow.org |
| API directories and marketplaces | 4 | apitracker.io, apis.io and others |
| Other | 5 | github.com/Serper-API, medium.com, youtube.com |
We ran a second check for UK readers at 00:45:40 SGT: "serper api key" with location: gb. The first two results were again the Serper home page and /api-keys. Next came another search API's home page, CrewAI docs and LangChain docs. The rest were a Medium comparison, a roundup of SERP APIs, Aikeedo docs and an h2oGPT GitHub issue.
People who search for Serper docs land, most of the time, in some framework's docs, and each framework describes Serper a little differently.
Developers who ask an AI coding assistant to write client code need docs that the assistant can read without a login. On our side, three public files do that job.
| File | What it holds |
|---|---|
| Tool catalog | All 17 search tools with parameters, defaults, limits, descriptions in three languages and the price; no key needed |
| OpenAPI file | Version 3.0.3, with 61 paths on November 5, 2026 |
/llms.txt | An index for language models; its first block gives the base URL, the header and a note that bad parameters come back with did_you_mean and allowed |
These files let an assistant check parameters before it writes code, instead of guessing from memory. A wrong guess costs a 422, as the tests below show. Serper's parameter rules live in the Playground, so an assistant mostly reads second-hand framework docs. For example, the CrewAI docs still say "n_results" defaults to 10, while the Playground's plain search menu now offers only 10.
Serper API docs parameters compared with EveryInfra
The short answer: both APIs take a JSON body and return JSON. But the parameter names, the way you pick a result type and the response fields differ. You cannot switch by changing the URL alone. We compared the plain search parameters in the Playground script with the parameters of the EveryInfra web tool. Our source was the free tool catalog, read at 00:16 SGT on November 5, 2026.
| Serper parameter | Meaning in the Playground | EveryInfra match | Note |
|---|---|---|---|
q | Query | q (web tool); keyword in the data catalog | Same name, same meaning |
gl | Country, two letters, default us | location | gl returns 422; location works at country level |
hl | Interface language, default en | None in web; language in web/search | hl returns 422 |
num | Results per page, 10 for plain search | num, up to 10 | Same name |
page | Page number from 1 | page from 1, each page is one charge | Same name |
type | URL segment: search, news, images | Body field tool: web, news, media | type in the body returns 422 |
tbs | Time range qdr:h/d/w/m/y | after, before (dates) | Convert to dates |
location | City, area or postal code; 100,029 entries | location works per country | Same name, different meaning |
autocorrect | Spelling fix, on by default | None | Drop it |
| — | — | site (one domain only) | Serper uses site: inside q |
Two rows need care. The first is location. The name is the same on both sides, but the meaning is not. In Serper, location is a precise place, and gl sets the country. In EveryInfra, location is the country, and a city counts as its country.
So when you move from Serper, copy the gl value into location, and drop Serper's old location value. The second is hl. The web tool has no language parameter. If you need one, use web/search in the data catalog. It takes keyword, country and language, and it costs $0.001389, twice the web price.
Result types change too. Serper puts the type in the URL. EveryInfra search has one endpoint, POST /api/v1/search, and the tool field in the body picks one of 17 tools. We built this table from the catalog text. A match means a similar purpose only, not identical results.
| Serper type | EveryInfra search tool | Data catalog options |
|---|---|---|
| search | web | web/search, bing/search, duckduckgo/search |
| news | news | google_news/search |
| images, videos | media (kind is image or video) | — |
| places, maps | places | — |
| shopping | shopping | google_shopping/search |
| scholar, patents | scholar (kind is papers or patents) | — |
| autocomplete | suggest | — |
| lens | lens | — |
| webpage | read | — |
| reviews, product-reviews | Not in search | Map and product reviews are in the data catalog by platform |
Because credit cost depends on type, Serper's price per 1,000 calls depends on type too. On the Starter pack, 1,000 credits cost $1.00. Maps and lens use 3 credits per call, so they cost $3.00 per 1,000 calls. On the Ultimate pack, 1,000 credits cost $0.30, so maps costs $0.90 per 1,000.
Every EveryInfra search tool except crosscheck costs $0.000694 per call. The chart puts the public unit prices side by side by type. For pack sizes and expiry rules, read Serper API Pricing Explained.
Your first request: curl on both sides
Here is a first Serper request in the Playground's POST form. We have no Serper key, so we did not run it. We built it from the page script and the CrewAI source.
# Serper: configuration example (not run for this article)
curl -s https://google.serper.dev/search \
-H "X-API-KEY: $SERPER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"serper api documentation","gl":"us","hl":"en","num":10}'Here is the same search on EveryInfra. We ran it at 00:18:40 SGT on November 5, 2026. It returned HTTP 200 in 1.55 seconds with 10 results, and billing.charged_usd was 0.000694.
# EveryInfra: run for this article
curl -s https://api.everyinfra.com/api/v1/search \
-H "Authorization: Bearer $EVERYINFRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tool":"web","q":"serper api documentation","location":"us","num":10}'Four things change: the URL, the header, gl becomes location, and hl goes away. One more detail is easy to miss. The docs put parameters at the top level of the body, as in {"tool":"web","q":...}. The remote MCP tool everyinfra_search puts them inside a params object, as in {"tool":"web","params":{"q":...}}. Both work, but you cannot mix them. The MCP section below shows what happens when you do.
Batches change too. Serper's Mini-batch takes up to 100 queries in one request. The EveryInfra search API takes one query per request. So a 100-keyword batch becomes 100 requests, sent within your per-minute limit. The default account limit is 60 requests per minute, so 100 keywords take about two minutes.
A speed pack raises the limit to 1,800 per minute (pricing). The cost stays the same: $0.000694 per query, or $0.0694 for 100. Single requests also fail one at a time. A bad parameter in one query returns one 422, and the other 99 still run.
Response fields: organic compared with results
The sample response on the Serper home page is one large object. Its top level has knowledgeGraph, organic, peopleAlsoAsk and relatedSearches. Each organic result has title, link, snippet and position, and some have sitelinks, date and attributes (serper.dev home page sample). The Playground removes searchParameters from the response before it shows results, so a real response also echoes the request. LangChain reads a different key for each type: organic for search, news for news, places for places and images for images (LangChain source).
The EveryInfra web response is {"id","tool","location","results","billing","quota"}. Each result has title, url and snippet, plus published when a date is known. On the page-2 call at 00:19:06 SGT, 3 of 10 results had published. The data catalog entry web/search returns more fields: title, url, text, rank, published_at, image_url and sub_links (data catalog). On the call at 00:20:09 SGT, 3 of 10 results had published_at.
| Use | Serper (search) | EveryInfra `web` | EveryInfra `web/search` |
|---|---|---|---|
| Title | organic[].title | results[].title | results[].title |
| Link | organic[].link | results[].url | results[].url |
| Snippet | organic[].snippet | results[].snippet | results[].text |
| Rank | organic[].position | Array order | results[].rank |
| Date | organic[].date (sometimes) | results[].published (sometimes) | results[].published_at (sometimes) |
| Knowledge panel, related questions | knowledgeGraph, peopleAlsoAsk | None | None |
| Cost of this call | Home page says credits come off per successful response; no documented response field | billing.charged_usd | billing.charged_usd |
The last row is about billing records. Serper's FAQ says "Every time you receive successful responses to queries, we will deduct credits from your account balance." When the balance reaches zero, new queries stop. To see what you spent, you open the Dashboard and Activity logs. Every EveryInfra response carries a billing object with charged, charged_usd and balance_after_usd.
A batch job can reconcile spend from the responses alone. Failed and empty calls show charged: false. The web tool does not return knowledge panels or related questions. Code that depends on those fields needs new logic, as the "When Serper is the better choice" section explains.
Test 1: a Serper-style request sent as it is
The most likely mistake after a switch is to change the URL and header but keep the old parameters. We made that mistake on purpose to see what the API does.
At 00:18:28 SGT we sent {"tool":"web","q":"serper api documentation","gl":"us","hl":"en","num":10}. The API returned HTTP 422 in 0.20 seconds. error.code was unknown_param. The message read "gl is not a parameter of tool web Supported: after, before, location, num, page, q, site".
The allowed list held the 7 valid parameters. The next_steps field read "Remove gl; the parameters are listed at catalog_url. Nothing was charged." The API reports one unknown parameter per call. We removed gl, kept hl and sent again at 00:18:47 SGT, and the same 422 came back with param: hl.
At 00:19:00 SGT we spelled location as locaton. The 422 came back in 0.11 seconds with did_you_mean: ["location"]. The first next step read "Rename locaton to location (did_you_mean), then resend. Nothing was charged." The second said to open a ticket if the catalog does list the parameter you sent.
At 00:19:14 SGT we put "type":"news" in the body, and it returned 422 because type is not a web parameter. At 00:19:20 SGT we sent kind: "videos" to the media tool. The error was invalid_value, with allowed set to image and video and did_you_mean set to video.

These errors exist so that an AI assistant can fix its own call. A 422 tells a program that a retry will not help, and allowed and did_you_mean tell an assistant what to change. HTTP defines 422 for a request that the server understands but cannot process (MDN: 422, defined in RFC 9110, section 15.5.21). That fits a wrong name or a wrong value. Our error body is not the RFC 9457 problem+json format. It is our own error object, and the guide to self-healing API errors explains each field.
Test 2: no key, the wrong header and rate limits
At 00:18:21 SGT we called web with no auth header. The API returned HTTP 401 in 0.12 seconds with code: unauthorized and message: Missing API key. The fault was account, retryable was false, and next_steps said "This API key is not accepted; ask the user for a working key (console: API keys). Nothing was charged." At 00:18:34 SGT we put a valid key in an X-API-KEY header, the Serper style. The result was the same 401 with the same message.
We must be honest about that second result. The error does not say "use Authorization: Bearer instead". If you arrive from Serper, change the header line yourself, as the table above shows.
For Serper we have no key, so we quote public posts. The n8n thread above shows HTTP 403 with the body {"message":"Unauthorized. Sign up for a free account.","statusCode":403}. CrewAI issue #633 from 2024 is titled "Unauthorized Error (403) when Using SERPER_API_KEY".
The cause was a key from a different service. For an empty balance, a commit note from October 23, 2026 records 400 {"message":"Not enough credits"} (neo1415/q #339). A task list from October 22, 2026 says "400 or 403" and marks it unconfirmed (dbhq-uk/gannet #12). We found no error code table on serper.dev.
Rate limits came up on their own. Our test key has its own cap of 10 calls per minute, and several writing jobs share it. We got HTTP 429 four times, at 00:19:55, 00:20:56, 00:22:38 and 00:24:51 SGT. Each had code: rate_limited, a retry_after of 5, 4, 22 and 9 seconds in turn, max_retries: 3 and the note "Nothing was charged.
Retry after retry_after seconds". Our script waited 60 seconds after any 429, and each next try worked. See MDN on Retry-After for the header and RFC 6585 for the 429 code.
| Case | Serper (public posts) | EveryInfra (our tests) | Charge |
|---|---|---|---|
| No key or a bad key | 403 Unauthorized. Sign up for a free account. | 401 unauthorized, Missing API key | None on either side |
| Wrong header | GET needs the key in the URL | X-API-KEY returns 401 | None |
| No balance | 400 Not enough credits (third-party record) | 402 quota_exhausted (docs) | None |
| Unknown parameter | Not documented | 422 unknown_param with allowed and did_you_mean | None |
| Bad value | Not documented | 422 invalid_value with allowed | None |
| Rate limit | 50 to 300 queries per second by pack (home page) | 429 rate_limited with retry_after | None |
| Empty result | Home page says only successful responses cost credits | 200, charged: false, reason: empty_result_refunded | None |
For the empty row, at 00:20:17 SGT we searched for a quoted string that does not exist. The API returned HTTP 200 with an empty results array, billing.charged: false, reason: empty_result_refunded and refund.status: succeeded. To reconcile failed and empty calls in your own records, see the failure refund reconciliation guide.
Test 3: one query in ten countries
Does location work after you move the gl value into it? From 00:22 to 00:26 SGT we sent the same query, "google search api", to web with num set to 10. The location values were us, gb, de, fr, es, mx, br, jp, sg and in. All ten calls returned HTTP 200 and cost $0.000694 each, $0.00694 in total. The US call returned 9 results, and each other call returned 10.
We used the 9 US URLs as the base and counted how many showed up in each other country. Singapore and the UK kept all 9. India, Japan and France kept 7. Brazil and Spain kept 6. Mexico and Germany kept only 5.
Germany added a personal site on a .de domain and a YouTube video. Japan added qiita.com, and Mexico added rapidapi.com. So the country parameter changes the results. English-speaking markets stay close to the US list, and other markets move further away, as you would expect from regional ranking.
This test also shows a limit. We tested at country level only, because EveryInfra location stops at the country. Serper goes down to city and postal code. If you need "local results for one ZIP code in New York", the EveryInfra web tool cannot do it.
The sample is also small: one query, one call per country. Ranks move over time. The overlap numbers prove that the parameter works. They do not measure long-term differences between countries.
Test 4: latency, and how fast errors come back
We split all 34 requests by outcome and measured curl's total time from Singapore, network included. The 21 paid requests took 0.98 to 3.48 seconds, with a median of 1.81 seconds. The fastest was suggest at 0.98 seconds. The slowest was the UK web call at 00:45:40 SGT, at 3.48 seconds. The empty result took 2.12 seconds, because the search did run.
The 11 rejected requests (401, 422 and 429) came back in 0.09 to 0.20 seconds. They stopped at the auth or parameter check and never searched. Serper's home page says "In general, all queries are returned in 1 to 2 seconds". We did not test Serper, so we make no speed claim.
The chart covers the first 29 requests. We made five more calls for this English edition. With them, the median of all 21 paid calls is 1.81 seconds.
| Time (SGT) | Call | Results | Seconds | Charge |
|---|---|---|---|---|
| 00:45:32 | suggest, "serper api docs" | 8 | 0.98 | $0.000694 |
| 00:45:40 | web, "serper api key", location: gb | 10 | 3.48 | $0.000694 |
| 00:45:51 | web, "SERPER_API_KEY", site: github.com | 10 | 2.67 | $0.000694 |
| 00:47:57 | web, built from a Serper body by the Python function below | 10 | 2.24 | $0.000694 |
| 00:53:26 | suggest, "serper api login" | 9 | 1.15 | $0.000694 |
Fast errors help the person who writes the code. A wrong parameter does not cost two seconds of waiting. A 422 with the fix arrives in a tenth of a second, so an assistant can fix and resend in one turn. The reverse also holds. A program that retries every non-200 response as "service busy" wastes retries on 422s. Retry only errors with retryable: true, such as 429 and 503.
Test 5: suggestions, news and the data catalog entry
Serper's autocomplete and news types map to the EveryInfra suggest and news tools. At 00:20:01 SGT, suggest for "serper api key" returned 10 rows. Each row has only a title, such as "serper api key free", "serper api key pricing", "serper api key crewai" and "get serper api key". It cost $0.000694.
At 00:21:28 SGT, news for "Model Context Protocol" returned 10 results, each with published. The values were relative, such as "17 hours ago" and "2 days ago". To sort by date in a database, convert them against the request time. Or use google_news/search in the data catalog, which returns published_at in UTC ISO 8601 for $0.000792.
At 00:20:09 SGT we tried the data catalog entry web/search for "serper api key", with keyword, country: us and language: en. It returned 10 results for $0.001389. This entry lets you set the result language. Its fields are text and rank, in the same structure as search results from other platforms in the catalog. That helps if you store web results next to other platform data. If you need only web results and no language setting, the web tool costs half as much.
The top three web/search results were the Serper home page, another search API's home page and serper.dev/api-keys. The seventh was a Clay community post on how to find the key in the Serper dashboard. We did not run the same query through web, so we do not compare result order. Do not expect the two entries to return the same list.
Setting the key in Python: the variable names differ
Keep the key in an environment variable, not in code. Both The Twelve-Factor App and the OWASP Secrets Management Cheat Sheet say so. For local work, python-dotenv reads a .env file, and .gitignore should list that file. The trouble is that Serper has no SDK of its own, so each framework picks its own variable name. We read the current source and docs of four frameworks.
| Framework | Serper variable name | Source |
|---|---|---|
LangChain (GoogleSerperAPIWrapper) | SERPER_API_KEY | LangChain reference |
CrewAI (SerperDevTool) | SERPER_API_KEY | CrewAI docs |
Haystack (SerperDevWebSearch) | SERPERDEV_API_KEY | Haystack source, docs |
| LiteLLM (search) | SERPER_API_KEY | LiteLLM docs |
Haystack adds "DEV" to the name. If one machine runs both LangChain and Haystack, set two variables, or write Secret.from_env_var("SERPER_API_KEY") in Haystack. Parameter names differ too. CrewAI's SerperDevTool uses country, locale and n_results, and its source maps them to gl, hl and num. It allows only the search and news types.
We read the main branch on the day after the 1.15.35 release of November 3, 2026. LangChain uses k, gl, hl and type. Haystack uses top_k and search_params. So each framework you connect has its own variable and parameter names to learn.
EveryInfra needs no special package in Python. Requests is enough. If you already have Serper-style request bodies in your code, a small function can turn them into EveryInfra bodies. We ran the function below at 00:47 SGT on November 5, 2026.
The input was {"q": "serper api key rotate", "gl": "gb", "hl": "en", "num": 10, "tbs": "qdr:y", "autocorrect": true}. The output was {"tool": "web", "q": "serper api key rotate", "location": "gb", "num": 10, "page": 1, "after": "2025-11-04"}. The call returned HTTP 200 with 10 results in 2.24 seconds for $0.000694. All 10 results had a published date after November 4, 2025, so the after filter worked.
import datetime as dt, os, requests
URL = "https://api.everyinfra.com/api/v1/search"
TYPE_TO_TOOL = {"search": "web", "news": "news", "images": "media", "videos": "media",
"places": "places", "maps": "places", "shopping": "shopping",
"scholar": "scholar", "patents": "scholar", "autocomplete": "suggest",
"lens": "lens", "webpage": "read"}
TBS_DAYS = {"qdr:h": 1, "qdr:d": 1, "qdr:w": 7, "qdr:m": 31, "qdr:y": 366}
def from_serper(serper_type, body, today=None):
"""Turn a Serper-style request into an EveryInfra search body."""
tool = TYPE_TO_TOOL[serper_type]
out = {"tool": tool, "q": body["q"]}
if tool == "web":
if "gl" in body:
out["location"] = body["gl"] # country code moves to location
out["num"] = min(int(body.get("num", 10)), 10)
out["page"] = int(body.get("page", 1))
if body.get("tbs") in TBS_DAYS:
today = today or dt.date.today()
out["after"] = (today - dt.timedelta(days=TBS_DAYS[body["tbs"]])).isoformat()
# hl, autocorrect and Serper's city-level location have no web-tool match: dropped
return out
body = from_serper("search", {"q": "serper api key rotate", "gl": "gb", "hl": "en",
"num": 10, "tbs": "qdr:y", "autocorrect": True})
r = requests.post(URL, json=body, timeout=90,
headers={"Authorization": f"Bearer {os.environ['EVERYINFRA_API_KEY']}"})
data = r.json()
if r.status_code != 200 and not data["error"].get("retryable"):
raise ValueError(data["error"]["next_steps"]) # 401 and 422: fix, do not retry
print(len(data.get("results", [])), data.get("billing", {}).get("charged_usd"))The function maps only the web path in detail. For media and scholar, add kind yourself and check the catalog first. The qdr:h row rounds up to one day, because after takes a date.
Setting the key in n8n: HTTP Request and MCP Client Tool
n8n has no built-in Serper node. The n8n.io Serper integration page returned 404 on November 5, 2026. People use the generic HTTP Request node. Put the key in a Header Auth credential with the name X-API-KEY (n8n HTTP Request credentials).
Set the method to POST. The 403 thread above came from a GET request. The settings below follow the n8n docs as we read them on November 5, 2026.
You can connect EveryInfra in two ways. The first is the same HTTP Request node. Set the Header Auth name to Authorization and the value to Bearer , a space, and your key. Set the URL to https://api.everyinfra.com/api/v1/search and copy the body from the curl example.
The second way is the MCP Client Tool node in an AI Agent workflow. Set the endpoint to https://api.everyinfra.com/mcp, pick Bearer auth and store the key in a credential. The agent then lists tools such as everyinfra_search and decides when to call them.
We did not run n8n for this article, so treat both setups as configuration examples from the n8n docs. For a full workflow that runs every morning, see the n8n daily report guide.
Setting the key in MCP clients: remote compared with local
Serper does not run an MCP server. GitHub has community servers, such as marcopesani/mcp-server-serper (MIT license, 171 stars, last push March 13, 2025) and garylab/serper-mcp-server (39 stars, last push November 10, 2025). Both start a local process on your computer with npx or uvx. The key goes into the env block of the client config file as SERPER_API_KEY. So the key sits in plain text in a config file on every machine that runs the client.
EveryInfra runs a remote MCP server at https://api.everyinfra.com/mcp. It uses the HTTP transport from the MCP specification, version 2026-07-28, and the key goes in the Authorization header. In Claude Code, one command adds it (Claude Code MCP docs). For other clients, see the MCP setup page and the remote MCP setup guide.
claude mcp add --transport http everyinfra https://api.everyinfra.com/mcp \
--header "Authorization: Bearer $EVERYINFRA_API_KEY"You can list the tools without a key. At about 00:21 SGT we called tools/list with no key and got 12 tools. The input for everyinfra_search is tool, one of 17, plus a params object. At 00:21:40 SGT we put q outside params on purpose.
The response was HTTP 200 with isError: true and the text "missing_param: q is required" and "Next steps: Add the required q. Nothing was charged." At 00:21:58 SGT we sent {"tool":"web","params":{"q":"serper api login","location":"us","num":5}}. It returned 5 results, the first was serper.dev/api-keys with the title "Log in to your account", and charged_usd was 0.000694, the same as a direct API call.
The MCP specification has a section on authorization for remote servers, and it recommends OAuth. We use a static Bearer key today. Every client can send one, but you must guard the key yourself, which is the topic of the next section. To pick a search tool type for an AI agent, see how to choose agent search tools.
Your key leaked: how to replace it on each side
Start with a recent real case. LiteLLM's incident report of October 23, 2026 tells this story. On May 27, 2026, someone committed a config file with an internal admin key to a public repo. The file came out about three hours later, and the key went on a "to revoke" list.
The commit stayed in public history until October 22, and because of human error the revoke step never happened. On October 10, 2026, an attacker used that four-month-old key, which still worked. From there the attacker reached a GitHub organization owner token. On October 21, LiteLLM's release pipeline published a compromised 1.107.0 (LiteLLM incident report).
| Date (2026) | What happened |
|---|---|
| May 27 | A config file with an admin key goes into a public repo |
| May 27, about 3 hours later | The file comes out; the key goes on a "to revoke" list |
| May 27 to October 22 | The commit stays in public history; the key is never revoked |
| October 10 | An attacker uses the old key, which still works |
| October 21 | A compromised 1.107.0 release ships |
The report names the lesson as "failing to rotate a potentially exposed key". On the same day, Railway said a third party may have accessed systems that store customer environment variables. It told all users to roll every environment variable stored in Railway, API keys included (Railway disclosure, October 21, 2026). The rule is simple.
Deleting a key from your code does not kill the key. You must make the old key invalid at the service. Here is how on each side.
On Serper, log in and open API Keys. Find the leaked key and click "Reset". After you confirm, the old key stops working at once, and the page shows a new key. Replace the key in each place that used it.
If one key served several places, all of them break together, because you cannot stop just one. Then open Activity logs, filter by that key and look for queries you do not know. If you turned on "Anonymize queries", the log shows counts and credits but no query text.
On EveryInfra, disable the leaked key in the console. Calls with it then return 401. A script can also do it. A key with the keys:write scope can call POST /portal/api/keys/{key_id}/disable. After that, only the account owner can turn the key back on in the console. Create a new key to replace it. If you made one key per environment, the other environments keep working.
Rotation, scopes and the IP allowlist can only change in the console, so a key cannot change its own settings (quickstart, auth section). Then check the call history for requests and spend during the leak. GET /api/v1/account also shows usage for the last 24 hours, and it costs nothing.
You can also plan for a leak before it happens. Split keys like this:
| Where the key runs | Settings to use |
|---|---|
| Production server | Its own key, with an IP allowlist for the server's addresses |
| CI | Its own key, with a small USD spend limit |
| Demo notebook or shared test | Its own key, with a small spend limit and an expiry date |
Then a leak of any one key means you disable that key only. The others keep working, and the loss stays under that key's limit.
No one else will catch the leak for you. GitHub secret scanning finds keys with known formats in repos, and it alerts partner services so they can revoke them. On November 5, 2026 we counted its supported patterns list: 207 providers, and neither Serper nor EveryInfra is among them. The TruffleHog v3.99.8 release of November 2, 2026 has detectors for three other SERP services but none for Serper. The default rules in Gitleaks have none either.
GitHub added two Anthropic key types to default push protection on October 13, 2026 (GitHub changelog). On October 28, 2026 it added AI-based push protection that judges context instead of fixed formats. That feature is off by default (GitHub changelog). Until a scanner knows your key format, users of GitHub Advanced Security can write custom patterns for it.
Public code shows how often the variable name travels. At 00:45:51 SGT we searched for "SERPER_API_KEY" with site: github.com. All 10 results were public GitHub pages. They included the CrewAI 403 issue, an h2oGPT issue titled "configure the serper api key?" and two community MCP servers.
An NVIDIA blueprint README and a Hermes Agent feature request from July 21, 2026 were also there. A variable name in public code is normal and safe. The risk starts when the value sits next to the name in a committed .env file, and no default scanner flags that value.
| Step after a leak | Serper | EveryInfra |
|---|---|---|
| Kill the old key | API Keys → Reset, immediate | Disable in the console; calls return 401 |
| Stop one place, keep the rest | No, the whole key resets | Yes, with one key per environment |
| Limit the damage | No per-key cap or IP setting | Product scopes, IP allowlist, per-key USD spend limit, expiry |
| Check calls during the leak | Activity logs filtered by key | Console call history, /api/v1/account |
| Automatic detection in public repos | Not in GitHub, TruffleHog or Gitleaks defaults | Not there either |
When Serper is the better choice
Here are the cases where Serper fits better than EveryInfra.
First, local search results by city, neighborhood or postal code. Serper's location list has 100,029 entries down to postal codes, while EveryInfra location stops at the country. For local SEO rank tracking or city-by-city result checks, Serper is the right tool. Second, results page modules such as knowledge panels, "People also ask" and related searches. Serper returns knowledgeGraph, peopleAlsoAsk and relatedSearches as fields.
The EveryInfra web tool returns title, link, snippet and date only. Third, high throughput. Serper's packs allow 50 to 300 queries per second. An EveryInfra account allows 60 per minute by default and up to 1,800 per minute with a speed pack (pricing). For hundreds of requests in one second, Serper is much stronger.
Fourth, map reviews and product reviews. Serper has reviews, product-reviews and maps as search types. EveryInfra search has no such tools, so you must find a matching capability in the data catalog for each platform, which costs more setup. Fifth, batch requests of up to 100 queries each.
Serper's Mini-batch supports them, and EveryInfra search takes one query per call. Sixth, a large code base already built on Serper's fields that only needs US English results. There, the code changes may cost more than you save, so wait. Consider a switch when you add search to an AI agent, or when you want one key for data, search and other capabilities.
Limits: when these findings do not hold
First, our Serper statements about the logged-in dashboard come from text in page scripts, not from screens we saw. We left out features that the script holds but does not show, such as types that only staff accounts can use. We also cannot see server features that the script does not mention. Second, the Serper error bodies come from third-party posts dated July 2025 to October 2026, and they may have changed. Third, we wrote the EveryInfra post-sign-up steps and key features from the page, docs, pricing page and OpenAPI file. We did not create a new account, and we did not call a scoped key to see the 403.
Fourth, our 34 calls used a test key with a cap of 10 calls per minute, unlike a normal account. Fifth, the location and latency tests are single samples from Singapore. Sixth, we do not compare result quality or coverage.
FAQ
How do I configure the serper api key in my app?
Set it as an environment variable, then check which name your framework reads. LangChain, CrewAI and LiteLLM read SERPER_API_KEY. Haystack reads SERPERDEV_API_KEY. An h2oGPT issue titled "configure the serper api key?" (h2oai/h2ogpt #914) shows the usual mistake.
The user pasted the key where the code expected a variable name. The app still failed, with a validation error that asked for a different key.
Is the Serper free tier 2,500 queries per month?
The Serper home page says "Get 2,500 free queries" and does not say "per month". A Hermes Agent feature request from July 21, 2026 calls it "2,500 free queries/month" (NousResearch/hermes-agent #68615). Check the dashboard after sign-up and do not plan on a monthly reset. The terms also forbid more than one account at a time. For how long 2,500 queries last, see Serper API Free Tier.
Why does CrewAI return 403 Unauthorized with my Serper key?
First, check that the key comes from serper.dev. In CrewAI issue #633, the user had a key from another service. Next, check that the variable name is SERPER_API_KEY, which CrewAI's source reads directly, and that the request uses POST. If the body says "Unauthorized. Sign up for a free account.", the key is wrong or missing. See CrewAI issue #633.
Is there a full serper api reference?
We found none on November 5, 2026. serper.dev/docs, /llms.txt and /openapi.json returned 404, and the GitHub organization has no SDK. Parameters, types and credit costs live in the Playground, which also writes the sample code. Framework docs from CrewAI, LangChain, Haystack, LiteLLM and Langflow each cover part of it.
Can I use Google or GitHub for serper api login?
We found no third-party login in the sign-up or login scripts. Serper login is email and password, with optional two-factor login. EveryInfra accepts email, GitHub or Google.
Get started: from key to first result
To try it, sign up at everyinfra.com. Star the two GitHub repos and claim the $1 trial credit in the console, valid for 24 hours. Create a key under API Keys, send the curl request above and read billing.charged_usd in the response. To read parameters for free, open the search tool catalog. For search capabilities in the data catalog, see the web data platform page, Bing, DuckDuckGo, Google News and Google Shopping. To try a call with one click in the console, use the infrastructure market.
If you are moving from Serper, change these items in this order. The last row is a test of your error handling, not a change.
| Step | Serper | EveryInfra |
|---|---|---|
| 1. URL | https://google.serper.dev/<type> | https://api.everyinfra.com/api/v1/search |
| 2. Header | X-API-KEY | Authorization: Bearer |
| 3. Result type | URL segment | tool field in the body |
| 4. Country | gl | location |
| 5. Language, spelling fix | hl, autocorrect | Drop them, or use web/search with language |
| 6. Time range | tbs | after, before |
| 7. Result link | organic[].link | results[].url |
| 8. Error test | — | Send one wrong parameter and check that your code reads allowed and did_you_mean |
For a wider view, see alternatives and why EveryInfra.
References
- Serper home page (pricing, FAQ, sample response), Serper, read 2026-11-05
- Create an account, Serper, read 2026-11-05
- Log in to your account, Serper, read 2026-11-05
- API Keys, Serper, read 2026-11-05 (login form when logged out)
- Playground, Serper, page script read 2026-11-05
- Terms of Service, Serper, updated 2024-05-29, read 2026-11-05
- Privacy Policy, Serper, reviewed 2025-11-05, read 2026-11-05
- Supported locations, Serper, read 2026-11-05
- Serper-API GitHub organization, GitHub, read 2026-11-05
- serper.dev web archive index, Internet Archive, read 2026-11-05
- postman-code-generators, Postman, read 2026-11-05
- Serper.dev authentication issues, n8n community, 2025-07-26
- Unauthorized Error (403) when Using SERPER_API_KEY #633, CrewAI, 2024-05-17
- configure the serper api key? #914, h2oGPT, 2023-10-01
- Add Serper.dev web search provider plugin #68615, Hermes Agent, 2026-07-21
- Research: read a spent plan or refused key as what it is #339, GitHub, 2026-10-23
- Live checks with real keys before release #12, GitHub, 2026-10-22
- Google Serper Search (SerperDevTool), CrewAI, read 2026-11-05
- serper_dev_tool.py, CrewAI, read 2026-11-05
- CrewAI 1.15.35, CrewAI, 2026-11-03
- GoogleSerperAPIWrapper, LangChain, read 2026-11-05
- google_serper.py, LangChain, read 2026-11-05
- SerperDevWebSearch, deepset, read 2026-11-05
- Haystack serperdev websearch.py, deepset, read 2026-11-05
- Serper Search, LiteLLM, read 2026-11-05
- Serper bundle, Langflow, read 2026-11-05
- HTTP Request node, n8n, read 2026-11-05
- HTTP Request credentials, n8n, read 2026-11-05
- MCP Client Tool node, n8n, read 2026-11-05
- n8n 2.46.6, n8n, 2026-11-04
- mcp-server-serper, GitHub, read 2026-11-05
- serper-mcp-server, GitHub, read 2026-11-05
- Model Context Protocol Specification 2026-07-28, MCP, read 2026-11-05
- MCP Authorization, MCP, read 2026-11-05
- Connect Claude Code to tools via MCP, Anthropic, read 2026-11-05
- RFC 9110: HTTP Semantics, IETF, 2022-06
- RFC 6585: Additional HTTP Status Codes, IETF, 2012-04
- RFC 9457: Problem Details for HTTP APIs, IETF, 2023-07
- 422 Unprocessable Content, MDN, read 2026-11-05
- Retry-After, MDN, read 2026-11-05
- Supported secret scanning patterns, GitHub, read 2026-11-05
- Secret scanning blocks Anthropic keys by default, adds Salesforce, GitHub, 2026-10-13
- Push protection beyond predefined secret patterns, GitHub, 2026-10-28
- Defining custom patterns for secret scanning, GitHub, read 2026-11-05
- TruffleHog v3.99.8, Truffle Security, 2026-11-02
- Gitleaks, Gitleaks, read 2026-11-05
- The attack chain behind the v1.107.0 LiteLLM release, LiteLLM, 2026-10-23
- Security Disclosure: Wednesday October 21st, Railway, 2026-10-21
- How to Retrieve Your API Key from Serper.dev Dashboard, Clay community, 2025-05-10
- Secrets Management Cheat Sheet, OWASP, read 2026-11-05
- The Twelve-Factor App: Config, 12factor.net, read 2026-11-05
- python-dotenv, PyPI, read 2026-11-05
- Requests Quickstart, Requests, read 2026-11-05
- EveryInfra search tool catalog, EveryInfra, read 2026-11-05 00:16 SGT
- EveryInfra data capability catalog, EveryInfra, read 2026-11-05 00:16 SGT
- EveryInfra OpenAPI, EveryInfra, read 2026-11-05
Reference BL-92