# Serply API Docs (full text) Every guide and API endpoint reference on serply.io/docs, concatenated as raw markdown. --- https://serply.io/docs/guides/introduction # Introduction An API to perform web searches and gather market data. Get fast and accurate real-time data. Serply is a simple to use API, but advanced enough to support special parameters such as languages, country and geographic locality. Get back JSON formatted data. Serply is the ultimate search API for developers, marketers, and data scientists. ## What Serply Can Do Here is some of what it can do: - **Web Search** - Perform web searches from Google and Bing for SEO/SERP data and with advertisement data - **News** - Search through thousands of news sources such as CNN, BBC, Reuters - **Products** - Search through thousands of products from the biggest e-commerce sites (Amazon) ## API Documentation Serply API Swagger Specs can be found at our [public GitHub Repo](https://github.com/serply-inc/openapi). In these docs you can try out the API and get code examples for various languages such as cURL, JavaScript, Python, C#, and Java. ## Example Code Check out our [Serply examples code](https://github.com/serply-inc/examples) for example code for calling the API with various languages. ## Next Steps - Read the [Quickstart Guide](/docs/guides/quickstart) to make your first API request - Check out our [code examples](/docs/guides/sdks) for various programming languages - Review [Authentication](/docs/guides/authentication) to understand how to secure your requests --- https://serply.io/docs/guides/quickstart # Quickstart Get up and running with Serply API in just a few minutes. ## Production Endpoint All API requests should be made to: ``` https://api.serply.io ``` ## Making Your First API Request An API key is a token that you provide when making API calls. Include the token in a header parameter called `X-Api-Key`. ### Using cURL ```bash curl --header 'X-Api-Key: YOUR_API_KEY' \ 'https://api.serply.io/v1/search/?q=google+search+api' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/search/?q=google+search+api', { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests from urllib.parse import quote_plus query = quote_plus('google search api') response = requests.get( f'https://api.serply.io/v1/search/?q={query}', headers={'X-Api-Key': 'YOUR_API_KEY'} ) data = response.json() print(data) ``` > **Send the query as `?q=`.** The older `/q=` spelling, with the query packed > into the path, is still accepted and returns the same results, but it cannot > carry every query. A literal `%` in the search text returns a 400, and a `?` > or `#` truncates the query there and silently drops the parameters that > follow, so `/v1/search/q=C%23+tutorial&num=5` searches for `C` and ignores > `num`. The `?q=` form handles all three correctly. Google Maps is the one > endpoint that takes its search text as a path segment instead of `q`; see the > [Google Maps](/docs/resources/google-maps) reference. ## Getting Your API Key 1. Sign up at [app.serply.io](https://app.serply.io) 2. Navigate to your dashboard 3. Copy your API key from the settings page You can view and manage your API keys in the Dashboard. ## What's Next? - Learn about [Authentication](/docs/guides/authentication) methods - Explore our [code examples](/docs/guides/sdks) for various programming languages - Check out the [API Reference](/docs) for all available endpoints --- https://serply.io/docs/guides/sdks # SDKs While Serply doesn't currently provide official SDK packages, we offer comprehensive code examples and integration guides for popular programming languages to help you get started quickly. ## Example Code Repository Check out our [Serply examples code repository](https://github.com/serply-inc/examples) for example code for calling the API with various languages including cURL, JavaScript, Python, C#, and Java. ## Using the REST API The Serply API is a RESTful API that can be called from any programming language that supports HTTP requests. All API requests should be made to: ``` https://api.serply.io ``` ### JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/search/?q=google+search+api', { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Python ```python import requests response = requests.get( 'https://api.serply.io/v1/search/?q=google+search+api', headers={'X-Api-Key': 'YOUR_API_KEY'} ) data = response.json() print(data) ``` ### cURL ```bash curl --header 'X-Api-Key: YOUR_API_KEY' \ 'https://api.serply.io/v1/search/?q=google+search+api' ``` ## Building Your Own SDK If you'd like to create a wrapper library for your preferred language, you can use our [OpenAPI specification](https://github.com/serply-inc/openapi) to generate client libraries. The API follows standard REST conventions and returns JSON responses. ## API Reference For complete API documentation, see the [API Reference](/docs) section which includes detailed information about all available endpoints, request parameters, and response formats. --- https://serply.io/docs/guides/authentication # Authentication All API requests to Serply require authentication using an API key. ## API Key Authentication An API key is a token that you provide when making API calls. Include the token in a header parameter called `X-Api-Key`. Authenticated requests must include an `X-Api-Key` header containing your subscription's API Key. ### Security Schema | Type | Header Name | Example Token | |------|-------------|---------------| | API Key | `X-Api-Key` | `2505adfbfbmshd20dee55ce912f8p17bd` | ### Example Request ```bash curl --header 'X-Api-Key: YOUR_API_KEY' \ 'https://api.serply.io/v1/search/?q=query' ``` In the following example, `X-Api-Key` represents the auth token for your account. ## Getting Your API Key You can view and manage your API keys in the [Dashboard](https://app.serply.io). ## Security Best Practices Be sure to keep your API keys secure. Do not share them in publicly accessible areas such as GitHub, client-side code, and so forth. - **Never commit your API key** to version control - Use environment variables to store your API key - Rotate your API key regularly - Only make API requests over HTTPS Also note that all API requests must be made over HTTPS. Calls made over plain HTTP will attempt to be automatically upgraded to HTTPS, though this use case is discouraged. ## Rate Limits API requests may be rate limited depending on your subscription plan and traffic patterns. The following response headers will be present in these cases: | Header | Description | |--------|-------------| | `x-ratelimit-requests-limit` | The maximum number of requests that the consumer is permitted to make | | `x-ratelimit-requests-remaining` | The number of requests remaining in the current rate limit window | When the rate limit is exceeded, an error is returned with the status "429 Too Many Requests". See the [Errors](/docs/guides/errors) guide for more details. --- https://serply.io/docs/guides/pagination # Pagination Serply passes Google's own pagination parameters straight through, so paging is offset-based: you ask for a page size with `num` and a starting offset with `start`. There are no cursors and no pagination envelope in the response. Both are ordinary query parameters, sent alongside `q`: ```bash curl --header 'X-Api-Key: YOUR_API_KEY' \ 'https://api.serply.io/v1/search/?q=coffee&num=10&start=0' ``` ## Parameters ### `num` The number of results to return. Honored exactly for small values - `num=5` returns 5 results. Note that Google serves roughly 10 organic results per page, and Serply does not stitch pages together for you. Asking for more than that does not produce more: `num=20`, `num=50`, and `num=100` all come back with about 10 results, the same as `num=10`. To collect 50 results you must make five requests at increasing offsets, not one request with `num=50`. ### `start` The zero-based offset into the result set. Omit it (or pass `0`) for the first page, `10` for the second, and so on. ```bash # first page 'https://api.serply.io/v1/search/?q=coffee&num=10&start=0' # second page 'https://api.serply.io/v1/search/?q=coffee&num=10&start=10' ``` ## The response has no pagination metadata Search responses contain no `pagination` object, no cursor, and no usable result count. The `total` field is present on some endpoints but is `null` for Google Search, so you cannot use it to compute a page count in advance. This means there is no way to know how many pages exist before you request them. You page until you stop getting results. ## Detecting the last page When you page past the end of the result set, the API returns `200 OK` with an empty `results` array: ```json { "results": [], "answers": [], "related_searches": { "text": [] } } ``` **An empty `results` array is not proof you have reached the end.** The same response appears during transient upstream failures, which cluster with `502` responses. The two are indistinguishable from a single request. To tell them apart, retry once or twice before concluding the page is empty. A genuine end-of-results is stable across retries; a transient failure is not. ## Collecting multiple pages A loop that stops on a confirmed-empty page: ```python import time import requests HEADERS = {'X-Api-Key': 'YOUR_API_KEY'} BASE = 'https://api.serply.io/v1/search' def fetch_page(query, start, num=10, retries=2): """Return one page. Retries so a transient blip isn't read as the end.""" for attempt in range(retries + 1): r = requests.get( f'{BASE}/?q={query}&num={num}&start={start}', headers=HEADERS, timeout=15, ) r.raise_for_status() results = r.json().get('results', []) if results: return results if attempt < retries: time.sleep(1) return [] def search_all(query, max_results=50, num=10): collected, seen = [], set() for start in range(0, max_results, num): page = fetch_page(query, start, num) if not page: break # confirmed empty after retries - end of results for item in page: if item['link'] not in seen: seen.add(item['link']) collected.append(item) return collected[:max_results] ``` Two details worth keeping: **Deduplicate by `link`.** Adjacent pages occasionally repeat a result, because Google reshuffles slightly between requests. In testing, four pages of 9 results yielded 34 unique links rather than 36. **Pin `X-Proxy-Location` across the whole loop.** If you omit it, individual requests may be served from different countries, and a page fetched from a German proxy will share almost nothing with one fetched from a US proxy. That inflates your unique-result count with what looks like new data but is really a different regional SERP: ```python HEADERS = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', } ``` Setting it once for every request in the loop keeps all pages on the same result set. The [Webhooks](/docs/guides/webhooks) guide has the measurements. **Always set an upper bound.** Since there is no total to check against, a loop without a `max_results` ceiling will keep paging - and keep billing - on any query where the empty-page signal is delayed. ## Billing Each page is a separate request, so **each page costs one credit**. Paging to 50 results costs 5 credits, not 1. Cached responses are free, and most endpoints cache for several minutes, so re-running the same paged query while developing does not bill again. See [Pricing](/pricing) for credit rates. ## Other endpoints `num` and `start` follow Google's query-string conventions and apply to the Google Search endpoint. Other resources use their own parameters - Reddit listings take `limit` and an `after` token, and Google Maps takes `num` after a `?`. Check the relevant page under [API Endpoints](/docs) before assuming `start` applies. --- https://serply.io/docs/guides/errors # Errors This API uses conventional HTTP response codes to indicate the success or failure of API requests. ## HTTP Status Codes In general: - **Codes in the 2xx range** indicate success - **Codes in the 4xx range** indicate an error that failed given the information provided (e.g., a required parameter was omitted, endpoint not found, etc.) - **Codes in the 5xx range** indicate an error with our API (these are rare) ### Common Status Codes | Code | Description | |------|-------------| | 200 | Success | | 400 | Bad Request - Invalid parameters | | 401 | Unauthorized - Invalid or missing API key | | 403 | Forbidden - API key doesn't have permission | | 404 | Not Found - Resource doesn't exist | | 429 | Too Many Requests - Rate limit exceeded | | 500 | Internal Server Error | ## Rate Limit Errors When the rate limit is exceeded, an error is returned with the status "429 Too Many Requests", using the same `detail` envelope as every other error: ```json { "detail": "Too many requests" } ``` Check the response headers for rate limit information: - `x-ratelimit-requests-limit` - Maximum requests allowed - `x-ratelimit-requests-remaining` - Requests remaining in current window ## Error Response Format Errors are returned in a consistent JSON format: a single `detail` key holding a human-readable message. ```json { "detail": "Human-readable error message" } ``` Examples of real responses: | Status | Body | |--------|------| | 401 | `{"detail": "API key missing"}` | | 401 | `{"detail": "Invalid API key"}` | | 404 | `{"detail": "Not Found"}` | ## Error Handling Always check the response status and handle errors appropriately: ```javascript try { const response = await fetch(url, { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail); } const data = await response.json(); // Handle successful response } catch (error) { console.error('API Error:', error.message); } ``` ```python import requests try: response = requests.get( 'https://api.serply.io/v1/search/?q=query', headers={'X-Api-Key': 'YOUR_API_KEY'} ) response.raise_for_status() data = response.json() # Handle successful response except requests.exceptions.HTTPError as e: error = e.response.json() print(f"API Error: {error.get('detail', e)}") ``` --- https://serply.io/docs/guides/webhooks # Webhooks **The Serply REST API does not currently support outbound webhooks.** There is no endpoint for registering a callback URL, and the API does not push events to your application. Serply's endpoints are synchronous: you make a request, and the results come back in that same response. There is no job queue to be notified about and no `search.completed` event, because the search has already completed by the time the call returns. If you need to be notified when something on the web changes, you build that loop on your side. The pattern is below. ## Polling instead Run your query on a schedule, compare against what you saw last time, and act only on the difference: ```python import json import time from pathlib import Path import requests HEADERS = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', # pin the region - see the warning below } STATE = Path('seen.json') def search(query, num=10): r = requests.get( f'https://api.serply.io/v1/search/?q={query}&num={num}', headers=HEADERS, timeout=15, ) r.raise_for_status() return r.json().get('results', []) def check(query): seen = set(json.loads(STATE.read_text())) if STATE.exists() else set() new = [item for item in search(query) if item['link'] not in seen] if new: notify(new) # Slack, email, a database write - whatever you need STATE.write_text(json.dumps(sorted(seen | {i['link'] for i in new}))) return new while True: check('your+query+here') time.sleep(900) # every 15 minutes ``` Storing the state is the part that matters. Without it you re-alert on the same results every cycle, and the notifications stop being read. ## Always pin `X-Proxy-Location` when monitoring This is the detail that breaks most polling loops, and it is not obvious. If you omit `X-Proxy-Location`, your request is served from whichever proxy is available, and the response comes back with `device_region` set to an empty string. Most consecutive calls then return the same results - until one is served from a different country, at which point you get an entirely different SERP. In testing, six identical queries three seconds apart returned the same nine results five times, and on the sixth returned a German result set sharing **zero** links with the previous five. A monitor built on that will announce that 100% of its results are new, then revert on the next cycle. Pinning the header removes the problem completely. The same six-run test with `X-Proxy-Location: US` returned identical results every time: ```python HEADERS = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', } ``` Any change in results is then a real change in the SERP, which is the only thing worth alerting on. See [Google Search](/docs/resources/google-search) for the full list of accepted regions. ## Why polling is cheaper than it sounds Credits are consumed per **successful, uncached** request. Cached responses cost nothing, and most endpoints cache for several minutes - Reddit endpoints cache for 10 minutes, and the Reddit comments response tells you directly with a `cached` field. The practical effect is that polling faster than the cache window does not cost more. A monitor that checks every minute pays for roughly the same number of credits as one that checks every ten, because the intervening calls are served from cache. That also means there is little value in polling very aggressively: you will receive the cached response until the window expires. Match your interval to how quickly the underlying data actually changes - for most search and news monitoring, 15 minutes to an hour is plenty. ## Handling failures in a long-running loop A polling loop runs unattended, so it needs to survive the transient errors a one-off script can ignore: - `502` means the upstream fetch or parse failed. It is transient - retry before treating it as an outage. - A `200` response with an empty `results` array is usually the same transient failure rather than a genuine "no results". Retry once or twice before recording that state, or your loop will alert on results "disappearing" and then "reappearing" on the next cycle. - `429` means you hit the rate limit. Back off and retry. See the [Errors](/docs/guides/errors) guide for the full error format. ## If you need push delivery Some use cases genuinely need push rather than poll - high query volumes, or alerting where a 15-minute delay is too long. If that describes your setup, contact [support](/support) and describe the workload; requirements like this help prioritize what gets built. --- https://serply.io/docs/guides/agent-skill # Agent Skill Serply publishes a [SKILL.md](https://serply.io/skill.md) file that teaches a coding agent how to use Serply: which endpoint to call for which job, the `X-Api-Key` auth header, the MCP tool names, and gotchas like Google Maps taking its parameters differently from every other endpoint. Once installed, an agent that already speaks the [Agent Skills](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) format (Claude Code, claude.ai, or the Claude API) picks it up automatically whenever a task needs live search results or page scraping - no need to paste API docs into the conversation. ## Install in Claude Code ```bash npx skills add serply-inc/skills ``` Or install it as a Claude Code plugin from the Serply marketplace: ```bash claude plugin marketplace add serply-inc/skills claude plugin install serply@serply-plugins ``` Or download the file directly into your project's or personal skills directory: ```bash mkdir -p .claude/skills/searching-with-serply curl -o .claude/skills/searching-with-serply/SKILL.md https://serply.io/skill.md ``` Use `~/.claude/skills/searching-with-serply/SKILL.md` instead of the project-local path to make it available across every project. Claude Code discovers and loads it automatically. ## Install in claude.ai Download the file above, then upload it as a custom Skill under **Settings > Features** (requires code execution to be enabled on your plan). ## What's in it - The REST endpoint table (Search, News, Maps, Images, Jobs, Scholar, Product, Video, Bing, eBay, Reddit, Page Fetch) with paths and response shapes - The `X-Api-Key` auth header and the free-credit signup link - The hosted MCP server address and its fourteen tool names with parameters (documented in full at [serply.io/mcp](/mcp)) - The gotchas worth knowing before you build against this API: send the query as a `?q=` query string rather than packed into the path, Google Maps takes its query as a path segment and Reddit takes ids as path segments, Maps does not support `X-Proxy-Location` or `X-User-Agent`, and Page Fetch is `POST`-only The full endpoint reference - every parameter, every response field - lives in the [API Endpoints](/docs) section; the Skill links out to it rather than duplicating it, so it stays short enough for an agent to read in one pass. ## Source The Skill's source is [github.com/serply-inc/skills](https://github.com/serply-inc/skills), at `skills/searching-with-serply/SKILL.md`. The same file is served at [serply.io/skill.md](https://serply.io/skill.md) and mirrored in this project at `.claude/skills/searching-with-serply/SKILL.md`. --- https://serply.io/docs/guides/strands # Strands Agents [Strands Agents](https://strandsagents.com/) is the open-source agent SDK from AWS. It loads tools from any MCP server through its `MCPClient`, so a Strands agent can use the hosted [Serply MCP server](/mcp) without an extra package: point the client at `https://api.serply.io/mcp`, pass your key in the `X-Api-Key` header, and the agent gets fourteen tools for live Google Search, Scholar, News, Video, Jobs, Maps, Bing, Amazon, Reddit and page scraping. Everything on this page was run against `strands-agents` 1.56.0 with `mcp` 2.1.1 on 2026-09-17. ## Prerequisites - Python 3.10 or newer, and `pip install strands-agents`. The `mcp` client library comes with it. - A Serply API key from [app.serply.io/users/sign_up](https://app.serply.io/users/sign_up). New accounts include 2,500 free credits, no card required. Export it as `SERPLY_API_KEY`; keep it out of source files (see the [Authentication guide](/docs/guides/authentication)). - A model provider Strands can reach. The default is Amazon Bedrock through the standard AWS credential chain. The Strands [quickstart](https://strandsagents.com/docs/user-guide/quickstart/) covers Bedrock and the other providers; the Serply side is the same for all of them. ## Connect the agent ```python import os from strands import Agent from strands.tools.mcp import MCPClient serply = MCPClient( url="https://api.serply.io/mcp", headers={"X-Api-Key": os.environ["SERPLY_API_KEY"]}, ) with serply: agent = Agent(tools=serply.list_tools_sync()) agent( "What are the three most cited papers on retrieval augmented " "generation? Give the citation count for each." ) ``` `MCPClient` takes the server URL and headers directly and opens a streamable HTTP session. `list_tools_sync()` asks the server for its tool list and turns each entry into a Strands tool, so the agent picks `google_scholar_search` from the tool descriptions on its own and reads the citation counts out of the result. The `with` block keeps the session open while the agent runs. A tool call outside it raises `MCPClientInitializationError` with the message "the client session is not running", which is the first thing to check if a call fails before it reaches Serply. If you prefer to let Strands manage the session, pass the client itself as a tool and skip the `with` block: ```python agent = Agent(tools=[serply]) ``` `MCPClient` implements the Strands `ToolProvider` interface, so the agent opens the connection when it needs the tools and closes it when it is done. Both forms load the same fourteen tools. ## Limit the tools the agent sees Fourteen tools is more than most agents need, and every tool description takes space in the model's context. Filter to the ones your agent should use, and prefix them if the agent has tools from other servers too: ```python serply = MCPClient( url="https://api.serply.io/mcp", headers={"X-Api-Key": os.environ["SERPLY_API_KEY"]}, tool_filters={ "allowed": [ "google_scholar_search", "google_news_search", "google_search", "scrape_url", ] }, prefix="serply", ) ``` `tool_filters` accepts `allowed` and `rejected` lists of tool names or compiled regular expressions. `prefix` renames the tools on the agent side to `serply_google_search` and so on; the server sees the original names. With the filter above the agent's tool list is four entries: `serply_google_scholar_search`, `serply_google_news_search`, `serply_google_search` and `serply_scrape_url`. ## Call a tool without a model To see what a tool returns before wiring it into an agent, call it through the client directly. This costs one credit and no model tokens: ```python with serply: result = serply.call_tool_sync( tool_use_id="scholar-1", name="google_scholar_search", arguments={"query": "retrieval augmented generation", "num": 3}, ) print(result["status"]) print(result["content"][0]["text"]) ``` ```text success 3 academic results for "retrieval augmented generation" 1. Retrieval-augmented generation for knowledge-intensive nlp tasks https://proceedings.neurips.cc/paper_files/paper/2020/hash/6b493230-Abstract.html P Lewis, E Perez, A Piktus, F Petroni... - Advances in neural ..., 2020 - proceedings.neurips.cc Cited by 29550 2. Retrieval-augmented generation for large language models: A survey https://arxiv.org/abs/2312.10997 Y Gao, Y Xiong, X Gao, K Jia, J Pan, Y Bi, Y Dai... - arXiv preprint arXiv ..., 2023 - arxiv.org Cited by 8096 ... ``` The text the model sees is exactly this block, which is why an agent given these tools can quote a citation count or a publication date instead of guessing. ## The tools | Tool | Use it for | |---|---| | `google_search` | Organic Google results; `site:` and the other [search operators](/docs/guides/search-operators) pass through in the query | | `google_scholar_search` | Papers with authors, venue, year and citation count | | `google_news_search` | News coverage with publisher and publication date | | `scrape_url` | Any public page as markdown or raw HTML, for reading a result in full | | `bing_search` | Bing organic results plus the ads Google does not return | | `google_video_search` | Video results | | `google_jobs_search` | Postings from Google's jobs index | | `google_maps_search` | Local businesses with address, rating, phone and hours | | `amazon_product_search` | Product listings with prices and availability | | `reddit_subreddit_posts`, `reddit_subreddit_about`, `reddit_user_posts`, `reddit_post`, `reddit_post_comments` | Reddit listings, profiles, posts and comment trees | Every parameter and return shape is documented on the [MCP Server](/mcp) page. ## What it costs A tool call bills the same as the equivalent REST call: 1 credit per successful, uncached request, against the same balance. See [pricing](/pricing) for plans beyond the free credits. ## Troubleshooting - **The tools list fine but every call returns `Invalid API key`.** The server accepts the connection and lists its tools before it checks the key; the key is checked on the first tool call, which then returns an error result containing `{"detail":"Invalid API key"}`. Confirm `SERPLY_API_KEY` is set in the environment the agent runs in. - **`MCPClientInitializationError: the client session is not running`.** The tool was called outside the `with` block, or you built the tool list inside one `with` block and ran the agent outside it. Either keep the agent call inside the block or use the `Agent(tools=[serply])` form. - **The connection times out on startup.** `MCPClient` waits 30 seconds for the server to answer `initialize`. Raise it with `startup_timeout=60` if you are behind a slow proxy; a healthy connection completes in well under a second. ## Related - [MCP Server](/mcp) - the server address, every tool's parameters, and the config for Claude Code, Claude Desktop and Cursor - [Strands MCP tools](https://strandsagents.com/docs/user-guide/concepts/tools/mcp-tools/) - the SDK's own reference for `MCPClient`, including OAuth and stdio servers - [Agent Skill](/docs/guides/agent-skill) - a `SKILL.md` that teaches Skill-compatible agents the REST API and the MCP server --- https://serply.io/docs/resources/google-search # Google Search Search Google and retrieve web search results in JSON format. ## Endpoint ``` GET /v1/search/{query} ``` ## Description The Google Search endpoint allows you to perform web searches and retrieve results from Google. The query parameter should be a URL-encoded query string that follows Google's search parameter format. For reference on Google search parameters, check out our [Google Search Operators guide](/docs/guides/search-operators). ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string. This should follow Google's search parameter format. **Examples:** - `q=search+api` - `q=search+api&num=10` - `q=search+api&num=10&gl=de` ## Query Parameters These are sent alongside `q` in the query string. ### `num` (optional) **Type:** `integer` The number of results to return. Small values are honored exactly (`num=5` returns 5 results). The ceiling is 10: Google serves about 10 organic results per page, so `num=20` or `num=100` still returns about 10. To get more, page with `start`. See the [Pagination guide](/docs/guides/pagination). ### `start` (optional) **Type:** `integer` The zero-based offset into the result set: `0` for the first page, `10` for the second. ### `gl` (optional) **Type:** `string` The country to target, as a two-letter country code (for example `us`, `de`, `nl`, `jp`). Serply passes it through to Google, so results are localized for that country. Unlike `X-Proxy-Location`, it accepts any country Google supports, not only the proxy regions listed below. If you send neither `gl` nor `X-Proxy-Location`, the request is served from whichever proxy region is available, and results can differ between calls. Set one of them when you need stable or localized results. ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. This determines the geographic location from which the search is performed. **Allowed values:** - `EU` - European Union - `CA` - Canada - `US` - United States - `IE` - Ireland - `GB` - United Kingdom - `FR` - France - `DE` - Germany - `SE` - Sweden - `IN` - India - `JP` - Japan - `KR` - South Korea - `SG` - Singapore - `AU` - Australia - `BR` - Brazil ### `X-User-Agent` (optional) **Type:** `string` Specify the device type for the search. Defaults to `desktop` if not provided. **Allowed values:** - `desktop` - Desktop browser (default) - `mobile` - Mobile device ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/search/?q=search+api' \ --header 'X-Api-Key: YOUR_API_KEY' \ --header 'X-Proxy-Location: US' \ --header 'X-User-Agent: desktop' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/search/?q=search+api', { headers: { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } response = requests.get( 'https://api.serply.io/v1/search/?q=search+api', headers=headers ) data = response.json() print(data) ``` ## Response The API returns a JSON object containing an array of search results, plus several other Google SERP feature arrays (ads, images, shopping, local places, etc.) that are populated only when Google's results page includes them. ### Response Structure ```json { "results": [ { "title": "Result Title", "description": "Result description text...", "position": 1, "realPosition": 1, "result_type": "organic", "metadata": { "display_url": "example.com" }, "link": "https://example.com" } ], "ads": [], "ads_count": 0, "answers": [], "image_results": [], "shopping_ads": [], "places": [], "local_businesses": [], "related_searches": [], "carousel": [], "company": {}, "total": null, "knowledge_graph": "", "related_questions": [], "carousel_count": 0, "ts": 0.9, "device_region": "", "device_type": null, "query": "search api" } ``` ### Response Fields - **`results`** (array): An array of organic search result objects - **`title`** (string): The title of the search result - **`description`** (string): The description/snippet of the search result - **`position`** (number): Rank within the result set, starting at 1 - **`realPosition`** (number): Same as `position` for organic results; differs when other SERP features are interleaved - **`result_type`** (string): Result classification, e.g. `organic` - **`metadata`** (object): Additional details — usually `display_url`; can include `attributes` (array of strings, e.g. a publish date) and, for local-business-style results, `rating`/`reviews` - **`link`** (string): The URL of the search result. Results served from certain buckets carry Google's own tracking params (`client=`, `ved=`, `usg=`, etc.) rather than a bare URL - **`total`** (number | null): Google's estimated result count. Usually `null` — Google only surfaces this on some result pages, so do not rely on it being populated - **`answers`** (array): Answer box content, if Google shows one for the query. Empty array when absent - **`ads`**, **`image_results`**, **`shopping_ads`**, **`places`**, **`local_businesses`**, **`related_searches`**, **`carousel`**, **`related_questions`** (arrays): Other SERP feature results, populated only when present on the page - **`ads_count`**, **`carousel_count`** (number): Counts for the corresponding arrays - **`company`** (object), **`knowledge_graph`** (string): Knowledge panel data, when present; otherwise empty - **`ts`** (number): Time in seconds the request took to complete - **`device_region`** (string): Proxy region used, if specified via `X-Proxy-Location` - **`device_type`** (string | null): Device type used for the search - **`query`** (string): The search query that was run ### Example Response ```json { "results": [ { "title": "The tutorial — Python 3.14.7 documentation", "description": "Python is an easy to learn, powerful programming language...", "position": 1, "realPosition": 1, "result_type": "organic", "metadata": { "display_url": "docs.python.org" }, "link": "https://docs.python.org/3/tutorial/" }, { "title": "Python Tutorial - W3Schools", "description": "Python is a popular programming language. Python can be used on a server to create web applications...", "position": 2, "realPosition": 2, "result_type": "organic", "metadata": { "display_url": "www.w3schools.com" }, "link": "https://www.w3schools.com/python/" } ], "ads": [], "ads_count": 0, "answers": [], "image_results": [], "shopping_ads": [], "places": [], "local_businesses": [], "related_searches": [], "carousel": [], "company": {}, "total": null, "knowledge_graph": "", "related_questions": [], "carousel_count": 0, "ts": 1.02, "device_region": "", "device_type": null, "query": "python tutorial" } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/bing-search # Bing Search Search Bing and retrieve web search results in JSON format, including advertisements and shopping ads. ## Endpoint ``` GET /v1/b/search/{query} ``` ## Description The Bing Search endpoint allows you to perform web searches and retrieve results from Bing. The query parameter should be a URL-encoded query string that follows Bing's search parameter format. For reference on Bing search operators, check out [Bing Search Operators Guide](https://seosly.com/blog/bing-search-operators/). ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string. This should follow Bing's search parameter format. **Examples:** - `q=search+api` - `q=instagra` ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. This determines the geographic location from which the search is performed. **Allowed values:** - `EU` - European Union - `CA` - Canada - `US` - United States - `IE` - Ireland - `GB` - United Kingdom - `FR` - France - `DE` - Germany - `SE` - Sweden - `IN` - India - `JP` - Japan - `KR` - South Korea - `SG` - Singapore - `AU` - Australia - `BR` - Brazil ### `X-User-Agent` (optional) **Type:** `string` Specify the device type for the search. Defaults to `desktop` if not provided. **Allowed values:** - `desktop` - Desktop browser (default) - `mobile` - Mobile device ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/b/search/?q=search+api' \ --header 'X-Api-Key: YOUR_API_KEY' \ --header 'X-Proxy-Location: US' \ --header 'X-User-Agent: desktop' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/b/search/?q=search+api', { headers: { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } response = requests.get( 'https://api.serply.io/v1/b/search/?q=search+api', headers=headers ) data = response.json() print(data) ``` ## Response The API returns a JSON object containing search results, advertisements, and shopping ads. ### Response Structure ```json { "ads": [ { "title": "Ad Title", "displayUrl": "https://example.com", "targetUrl": "https://example.com/target", "content": "Ad description...", "position": 1, "area": "top", "realPosition": 1 } ], "adsCount": 2, "shoppingAds": [ { "title": "Product Title", "price": "$99.99", "originalPrice": null, "advertiser": "Store Name", "targetUrl": "https://example.com/product", "image": "https://example.com/image.jpg" } ], "results": [ { "title": "Result Title", "description": "Result description...", "realPosition": 2, "link": "https://example.com" } ], "location": { "htmlLang": "en" }, "ts": 1.246995449066162, "device_region": "", "device_type": null, "query": "search api" } ``` ### Response Fields - **`ads`** (array): An array of advertisement objects - **`title`** (string): The title of the advertisement - **`displayUrl`** (string): The display URL shown to users - **`targetUrl`** (string): The actual target URL of the advertisement - **`content`** (string): The description/content of the advertisement - **`position`** (integer): The position of the ad in the ad list - **`area`** (string): The area where the ad appears (`top` or `bottom`) - **`realPosition`** (integer): The actual position in the search results - **`adsCount`** (integer): The total number of advertisements - **`shoppingAds`** (array): An array of shopping advertisement objects - **`title`** (string): The product title - **`price`** (string): The product price - **`originalPrice`** (string | null): The original price if on sale - **`advertiser`** (string): The name of the advertiser/store - **`targetUrl`** (string): The URL to the product page - **`image`** (string): The product image URL - **`results`** (array): An array of organic search result objects - **`title`** (string): The title of the search result - **`description`** (string): The description/snippet of the search result - **`realPosition`** (integer): The position in the search results - **`link`** (string): The URL of the search result - **`location`** (object): Location information - **`htmlLang`** (string): The HTML language code - **`ts`** (number): Timestamp of the search - **`device_region`** (string): Proxy region used, if specified via `X-Proxy-Location` - **`device_type`** (string | null): The device type used for the search - **`query`** (string): The search query that was run ### Example Response ```json { "ads": [ { "title": "amazon.in - Buy Mobiles at Amazon", "displayUrl": "https://www.amazon.in/Electronics/Home", "targetUrl": "https://www.bing.com/aclk?...", "content": "Explore latest selection of Mobiles, Tablets, Cameras & More. Pay on Delivery.", "position": 1, "area": "top", "realPosition": 1 } ], "adsCount": 2, "shoppingAds": [ { "title": "Apple iPhone 13 (128 GB, Green)", "price": "₹ 66,990.00", "originalPrice": null, "advertiser": "Vijay Sales", "targetUrl": "https://www.bing.com/aclk?...", "image": "data:image/svg+xml;charset=utf8,..." } ], "results": [ { "title": "iPhone 14 and iPhone 14 Plus - Apple (IN)", "description": "iPhone 14 has the same super-speedy chip that's in iPhone 13 Pro. A15 Bionic, with a 5‑core …", "realPosition": 2, "link": "https://www.apple.com/in/iphone-14/" } ], "location": { "htmlLang": "en" }, "ts": 1.246995449066162, "device_type": null } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/google-video # Google Video Search Google Video to retrieve video results in JSON format. ## Endpoint ``` GET /v1/video/{query} ``` ## Description The Google Video Search endpoint allows you to search for videos on Google and retrieve results. The query parameter should be a URL-encoded query string that follows Google's search parameter format. For reference on Google search parameters, check out our [Google Search Operators guide](/docs/guides/search-operators). ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string. This should follow Google's search parameter format. **Examples:** - `q=iphone+reviews` - `q=search+api&num=100` ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. This determines the geographic location from which the search is performed. **Allowed values:** - `EU` - European Union - `CA` - Canada - `US` - United States - `IE` - Ireland - `GB` - United Kingdom - `FR` - France - `DE` - Germany - `SE` - Sweden - `IN` - India - `JP` - Japan - `KR` - South Korea - `SG` - Singapore - `AU` - Australia - `BR` - Brazil ### `X-User-Agent` (optional) **Type:** `string` Specify the device type for the search. Defaults to `desktop` if not provided. **Allowed values:** - `desktop` - Desktop browser (default) - `mobile` - Mobile device ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/video/?q=iphone+reviews' \ --header 'X-Api-Key: YOUR_API_KEY' \ --header 'X-Proxy-Location: US' \ --header 'X-User-Agent: desktop' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/video/?q=iphone+reviews', { headers: { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } response = requests.get( 'https://api.serply.io/v1/video/?q=iphone+reviews', headers=headers ) data = response.json() print(data) ``` ## Response The API returns a JSON object containing an array of video search results, plus the same SERP-feature arrays as [Google Search](/docs/resources/google-search) (ads, images, shopping, etc.), populated only when Google's results page includes them. ### Response Structure ```json { "results": [ { "title": "Video Title", "link": "https://example.com/video", "description": "", "realPosition": 1 } ], "ads": [], "ads_count": 0, "answers": [], "shopping_ads": [], "places": [], "related_searches": [], "image_results": [], "carousel": [], "company": {}, "total": null, "knowledge_graph": "", "related_questions": [], "carousel_count": 0, "ts": 0.9, "device_region": "", "device_type": null, "query": "iphone reviews" } ``` ### Response Fields - **`results`** (array): An array of video result objects - **`title`** (string): The title of the video result - **`link`** (string): The URL of the video result. Often carries Google's own tracking params (`sa=`, `ved=`, `usg=`) rather than a bare URL - **`description`** (string): Usually empty — Google's video results rarely include a snippet - **`realPosition`** (number): Rank within the result set, starting at 1 - **`total`** (number | null): Google's estimated result count. Usually `null` - **`answers`** (array): Answer box content, if present. Empty array when absent - **`ads`**, **`shopping_ads`**, **`places`**, **`related_searches`**, **`image_results`**, **`carousel`**, **`related_questions`** (arrays): Other SERP feature results, populated only when present on the page - **`ads_count`**, **`carousel_count`** (number): Counts for the corresponding arrays - **`company`** (object), **`knowledge_graph`** (string): Knowledge panel data, when present; otherwise empty - **`ts`** (number): Time in seconds the request took to complete - **`device_region`** (string): Proxy region used, if specified via `X-Proxy-Location` - **`device_type`** (string | null): Device type used for the search - **`query`** (string): The search query that was run ### Example Response ```json { "results": [ { "title": "Python Full Course for Beginners - YouTube", "link": "https://www.youtube.com/watch?v=_uQrJ0TkZlc", "description": "", "realPosition": 1 }, { "title": "Python Full Course for free - YouTube", "link": "https://www.youtube.com/watch?v=ix9cRaBkVe0", "description": "", "realPosition": 2 } ], "ads": [], "ads_count": 0, "answers": [], "shopping_ads": [], "places": [], "related_searches": [], "image_results": [], "carousel": [], "company": {}, "total": null, "knowledge_graph": "", "related_questions": [], "carousel_count": 0, "ts": 1.1, "device_region": "", "device_type": null, "query": "python tutorial" } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/google-images # Google Images Search Google Images to retrieve image results in JSON format. ## Endpoint ``` GET /v1/image/{query} ``` ## Description The Google Images Search endpoint returns image results for a query, each with a thumbnail set, the page the image was found on, and the original image's URL and dimensions. Results arrive in the **`image_results`** array. The `results` array that the other search endpoints populate is always empty here — see [Response](#response) below. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` The search terms. Send them as the `q` query parameter; a bare term as a path segment is also accepted and returns the same results: **Examples:** - `q=vintage+bicycle` - `vintage+bicycle` Unlike [Google Search](/docs/resources/google-search), this endpoint takes only the search terms. Every call returns up to 20 images, and there is no pagination parameter. **Prefer the `q=` form if you pass anything else.** Additional Google parameters are dropped only when the path is a query string this endpoint can parse, which means it needs the `q=` key: `q=origami+crane&num=5` searches for *origami crane* and ignores `num`. Appended to a bare term, the same parameter becomes part of the search — `origami+crane&num=5` looks for the literal text `origami crane&num=5` and returns unrelated images rather than an error. ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. This determines the geographic location from which the search is performed. **Allowed values:** - `EU` - European Union - `CA` - Canada - `US` - United States - `IE` - Ireland - `GB` - United Kingdom - `FR` - France - `DE` - Germany - `SE` - Sweden - `IN` - India - `JP` - Japan - `KR` - South Korea - `SG` - Singapore - `AU` - Australia - `BR` - Brazil ### `X-User-Agent` (optional) **Type:** `string` Specify the device type for the search. Defaults to `desktop` if not provided. **Allowed values:** - `desktop` - Desktop browser (default) - `mobile` - Mobile device ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/image/?q=vintage+bicycle' \ --header 'X-Api-Key: YOUR_API_KEY' \ --header 'X-Proxy-Location: US' \ --header 'X-User-Agent: desktop' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/image/?q=vintage+bicycle', { headers: { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } }); const data = await response.json(); console.log(data.image_results); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY', 'X-Proxy-Location': 'US', 'X-User-Agent': 'desktop' } response = requests.get( 'https://api.serply.io/v1/image/?q=vintage+bicycle', headers=headers ) data = response.json() print(data['image_results']) ``` ## Response The API returns a JSON object whose `image_results` array carries the images. The other SERP-feature arrays are present for consistency with the rest of the API and are empty on this endpoint. ### Response Structure ```json { "image_results": [ { "image": { "src": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSpyW8...&s", "alt": "Ladies Classic 7-Speed Vintage Bike Red - Reid Bikes" }, "link": { "href": "https://shop.reidbikes.com/products/ladies-classic-7-speed-vintage-bike-red", "title": "Ladies Classic 7-Speed Vintage Bike Red - Reid Bikes", "domain": "shop.reidbikes.com" }, "original_image": { "src": "http://shop.reidbikes.com/cdn/shop/files/ladies-classic-vintage-bike-red.png", "width": "1170", "height": "764", "file_format": "image/png" }, "thumbnails": { "small": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSpyW8...&s", "medium": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRn7d-...&s", "large": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQZilx...&s" } } ], "results": [], "ads": [], "ads_count": 0, "answers": [], "shopping_ads": [], "places": [], "related_searches": [], "carousel": [], "company": {}, "total": 20, "ts": 1.27, "device_region": "", "device_type": null, "query": "q=vintage+bicycle" } ``` ### Response Fields - **`image_results`** (array): An array of image result objects - **`image`** (object): The result as shown on the results page - **`src`** (string): Thumbnail URL, served from Google's `encrypted-tbn0.gstatic.com` cache rather than the source site - **`alt`** (string): Alt text, generally the source page's title - **`link`** (object): Where the image was found - **`href`** (string): URL of the page hosting the image - **`title`** (string): Title of that page - **`domain`** (string): Hostname of that page - **`original_image`** (object): The full-size image on the source site - **`src`** (string): Direct URL to the original image. Served by the source site, so it may be `http://`, may be hotlink-protected, and may 404 independently of the search result - **`width`**, **`height`** (string): Pixel dimensions of the original, as strings - **`file_format`** (string): MIME type, e.g. `image/jpeg`, `image/png`, `image/webp`, `image/svg+xml`. Google does not always report the subtype, in which case this is the bare string `image/` — treat it as unknown rather than parsing it, and fall back to the extension on `src` if you need the real format - **`thumbnails`** (object): Three cached preview sizes — **`small`**, **`medium`**, **`large`** (strings). All are Google-hosted and safe to hotlink - **`results`** (array): Always empty on this endpoint. Image results are in `image_results` - **`total`** (number): Number of images returned, up to 20 - **`ts`** (number): Time in seconds the request took to complete - **`device_region`** (string): Proxy region used, if specified via `X-Proxy-Location` - **`device_type`** (string | null): Device type used for the search - **`query`** (string): The search terms as sent, echoed verbatim including the `q=` prefix, so `?q=vintage+bicycle` reports `"q=vintage+bicycle"`, not `"vintage bicycle"` - **`ads`**, **`answers`**, **`shopping_ads`**, **`places`**, **`related_searches`**, **`carousel`** (arrays), **`company`** (object), **`ads_count`** (number): Present for consistency with the other search endpoints; empty here ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/google-jobs # Google Jobs Search Google Jobs to retrieve job listings in JSON format. > **Note:** Currently only supports jobs in North America. ## Endpoint ``` GET /v1/job/search/{query} ``` ## Description The Google Jobs Search endpoint allows you to search for job listings on Google. The query parameter should be a URL-encoded query string containing the position title and optionally the location. For reference on Google search parameters, check out our [Google Search Operators guide](/docs/guides/search-operators). ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` Position Title and Location (optional). The query should be URL-encoded. **Examples:** - `q=nurse+practitioner` - `q=data+analyst+work+from+home` ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/job/search/?q=nurse+practitioner' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/job/search/?q=nurse+practitioner', { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY' } response = requests.get( 'https://api.serply.io/v1/job/search/?q=nurse+practitioner', headers=headers ) data = response.json() print(data) ``` ## Response The API returns a JSON object containing an array of job listings. ### Response Structure ```json { "jobs": [ { "position": "Software Engineer", "employer": "Company Name", "employer_link": "https://example.com/company/company-name", "location": "San Francisco, CA", "link": "https://example.com/job/123", "posted_at": "2026-08-14" } ] } ``` ### Response Fields - **`jobs`** (array): An array of job listing objects - **`position`** (string | null): The job title/position name - **`employer`** (string | null): The name of the employer/company - **`employer_link`** (string | null): A link to the employer's profile - **`location`** (string | null): The job's listed location - **`link`** (string | null): The URL to view/apply for the job - **`posted_at`** (string | null): When the listing was posted, if available - **`error`** (string, only present when no listings were found): Set instead of returning an empty `jobs` array silently ### Example Response ```json { "jobs": [ { "position": "Nurse Practitioner", "employer": "Healthcare System", "employer_link": "https://example.com/company/healthcare-system", "location": "New York, NY", "link": "https://www.example.com/jobs/12345", "posted_at": "2026-08-13" } ] } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/google-scholar # Google Scholar Search for academic papers, articles, and research results in JSON format. ## Endpoint ``` GET /v1/scholar/{query} ``` ## Description The Scholar Search endpoint allows you to search for academic papers, articles, and research across the scholarly literature. The query parameter should be a URL-encoded query string that follows Google's search parameter format — `q=` for the search terms, plus optional `num=` (results per page, up to 200) and `start=` (result offset for pagination). Scholar results are drawn from a global scholarly index and are not geo-differentiated, so the `X-Proxy-Location` and `X-User-Agent` headers below are accepted but do not change the results. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string. This should follow Google's search parameter format. **Examples:** - `q=high+frequency+trading` - `q=machine+learning+neural+networks` ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. Available options: - `EU` - European Union - `CA` - Canada - `US` - United States - `IE` - Ireland - `GB` - United Kingdom - `FR` - France - `DE` - Germany - `SE` - Sweden - `IN` - India - `JP` - Japan - `KR` - South Korea - `SG` - Singapore - `AU` - Australia - `BR` - Brazil ### `X-User-Agent` (optional) **Type:** `string` Specify the user agent type. Available options: - `desktop` - Desktop user agent (default) - `mobile` - Mobile user agent ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/scholar/?q=high+frequency+trading' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/scholar/?q=high+frequency+trading', { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY' } response = requests.get( 'https://api.serply.io/v1/scholar/?q=high+frequency+trading', headers=headers ) data = response.json() print(data) ``` ## Response The API returns a JSON object with the articles under the `articles` key. ### Response Structure ```json { "articles": [ { "title": "Paper Title", "link": "https://doi.org/10.1000/example", "id": "W2238750598", "description": "Author One, Author Two - Journal Name, 2014", "author": { "names": "Author One, Author Two - Journal Name, 2014", "authors": [ { "name": "Author One", "link": "https://openalex.org/A5004251974" } ] }, "doc": { "link": "https://example.edu/paper.pdf", "type": "PDF" }, "extras": { "citations": { "count": 1223, "link": "https://api.openalex.org/works?filter=cites:W2238750598" } } } ], "ts": 1234567890, "device_region": "US", "device_type": "desktop" } ``` ### Response Fields - **`articles`** (array): An array of article objects - **`title`** (string): The title of the academic paper or article - **`link`** (string): The DOI or landing page URL of the paper - **`id`** (string): A stable identifier for the work - **`description`** (string): The byline — authors, venue, and year - **`author`** (object): `names` is the byline as one string; `authors` is an array of `{name, link}` objects, where `link` is the author's profile URL when available - **`doc`** (object, optional): An open-access full-text link when one exists, with `link` and `type` (e.g. `"PDF"`) - **`extras.citations`** (object, optional): `count` is how many works cite this one; `link` lists the citing works - **`ts`** (number): Time taken to serve the search, in seconds - **`device_region`** (string): The device region used for the search - **`device_type`** (string): The device type used for the search ### Example Response ```json { "articles": [ { "title": "High-Frequency Trading and Price Discovery", "link": "https://doi.org/10.1093/rfs/hhu032", "id": "W2238750598", "description": "Jonathan Brogaard, Terrence Hendershott, Ryan Riordan - Review of Financial Studies, 2014", "author": { "names": "Jonathan Brogaard, Terrence Hendershott, Ryan Riordan - Review of Financial Studies, 2014", "authors": [ { "name": "Jonathan Brogaard", "link": "https://openalex.org/A5004251974" }, { "name": "Terrence Hendershott", "link": "https://openalex.org/A5059226754" }, { "name": "Ryan Riordan", "link": "https://openalex.org/A5069996199" } ] }, "doc": { "link": "https://www.econstor.eu/bitstream/10419/154035/1/ecbwp1602.pdf", "type": "PDF" }, "extras": { "citations": { "count": 1223, "link": "https://api.openalex.org/works?filter=cites:W2238750598" } } }, { "title": "High frequency trading and the new market makers", "link": "https://doi.org/10.1016/j.finmar.2013.06.006", "id": "W2060331219", "description": "Albert J. Menkveld - Journal of Financial Markets, 2013", "author": { "names": "Albert J. Menkveld - Journal of Financial Markets, 2013", "authors": [ { "name": "Albert J. Menkveld", "link": "https://openalex.org/A5046473475" } ] }, "doc": { "link": "http://papers.tinbergen.nl/11076.pdf", "type": "PDF" }, "extras": { "citations": { "count": 781, "link": "https://api.openalex.org/works?filter=cites:W2060331219" } } } ], "ts": 0.42, "device_region": "US", "device_type": "desktop" } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/google-news # Google News Search Google News to retrieve news articles from thousands of sources in JSON format. ## Endpoint ``` GET /v1/news/{query} ``` ## Description The Google News Search endpoint allows you to search for news articles from Google News. The query parameter should be a URL-encoded query string. News can be filtered by country and language using the `ceid` parameter. By default each entry's `link` is Google's own redirect (`https://news.google.com/rss/articles/...`), which is how Google News publishes its feed. Set `resolve_links=true` to have Serply follow those redirects for you and return the publisher's URL instead. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string for Google News search. You can use simple queries or include country/language filters. **Examples:** - Simple query: `q=president` - Multiple keywords: `q=news+about+president+trump` - Filtered by country and language: - US news in English: `q=trump&ceid=US:en` - Great Britain news in English: `q=trump&ceid=GB:en` ## Query Parameters ### `resolve_links` (optional) **Type:** `boolean` **Default:** `false` When `true`, every entry's `link` is rewritten from the `news.google.com/rss/articles/...` redirect to the article's real URL on the publisher's site. The original redirect is kept as `google_link`, each entry gets a `link_resolved` boolean, and the response gains a `links_resolved` count. Links inside `links[]`, `sub_articles[]` and the `summary` HTML are rewritten as well. Resolution needs one extra fetch per article, done in parallel on Serply's servers, so expect the request to take roughly 2 to 3 seconds longer. Any link that cannot be resolved is returned unchanged with `link_resolved: false`. The flag is an ordinary query parameter, sent alongside the other news parameters: ``` GET /v1/news/?q=tesla&resolve_links=true GET /v1/news/?q=tesla&ceid=US:en&resolve_links=true ``` ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. Available options: - `EU` - European Union - `CA` - Canada - `US` - United States - `IE` - Ireland - `GB` - United Kingdom - `FR` - France - `DE` - Germany - `SE` - Sweden - `IN` - India - `JP` - Japan - `KR` - South Korea - `SG` - Singapore - `AU` - Australia - `BR` - Brazil ### `X-User-Agent` (optional) **Type:** `string` Specify the user agent type. Available options: - `desktop` - Desktop user agent (default) - `mobile` - Mobile user agent ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/news/?q=stock+market' \ --header 'X-Api-Key: YOUR_API_KEY' ``` With publisher URLs instead of Google redirects: ```bash curl --request GET \ --url 'https://api.serply.io/v1/news/?q=stock+market&resolve_links=true' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch('https://api.serply.io/v1/news/?q=stock+market', { headers: { 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests headers = { 'X-Api-Key': 'YOUR_API_KEY' } response = requests.get( 'https://api.serply.io/v1/news/?q=stock+market', headers=headers ) data = response.json() print(data) ``` ## Response The API returns a JSON object containing a `feed` object with news feed metadata and an `entries` array of news articles. ### Response Structure ```json { "feed": { "title": "News Feed Title", "generator": "Google News", "generator_detail": {}, "link": "https://news.google.com/...", "links": {}, "language": "en", "publisher": "Google News", "publisher_detail": "...", "rights": "...", "rights_detail": "...", "updated": "2024-01-01T00:00:00Z", "updated_parsed": "...", "subtitle": "...", "subtitle_detail": "...", "entries": [ { "title": "Article Title", "title_detail": {}, "links": [{"href": "https://news.google.com/rss/articles/CBMi..."}], "link": "https://news.google.com/rss/articles/CBMi...", "id": "article-id", "guidislink": false, "published": "2024-01-01T00:00:00Z", "published_parsed": "...", "summary": "Article summary...", "summary_detail": {}, "source": "Source Name", "sub_articles": "..." } ] }, "entities": [ { "title": "Article Title", "links": [] } ] } ``` ### Response Fields - **`feed`** (object): News feed metadata and entries - **`title`** (string): Feed title - **`generator`** (string): Feed generator name - **`link`** (string): Feed URL - **`language`** (string): Feed language - **`publisher`** (string): Publisher name - **`updated`** (string): Last update timestamp - **`entries`** (array): Array of news article objects - **`title`** (string): Article title - **`link`** (string): Article URL. A `news.google.com/rss/articles/...` redirect by default; the publisher's URL when `resolve_links=true` - **`google_link`** (string): The original Google redirect. Only present when `resolve_links=true` - **`link_resolved`** (boolean): Whether `link` was rewritten to the publisher's URL. Only present when `resolve_links=true` - **`summary`** (string): Article summary/description - **`published`** (string): Publication date - **`source`** (string): News source name - **`links_resolved`** (integer): How many entries were rewritten to publisher URLs. Only present when `resolve_links=true` - **`entities`** (array): Array of entity objects with titles and links ### Example Response ```json { "feed": { "title": "stock market - Google News", "generator": "Google News", "link": "https://news.google.com/rss/search?q=stock+market", "language": "en", "publisher": "Google News", "updated": "2024-01-15T12:00:00Z", "entries": [ { "title": "Stock Market Reaches New High - Financial Times", "link": "https://news.google.com/rss/articles/CBMi...?oc=5", "summary": "Stock Market Reaches New High  Financial Times", "published": "Mon, 15 Jan 2024 10:00:00 GMT", "source": {"href": "https://www.ft.com", "title": "Financial Times"} } ] }, "entities": [ { "title": "Stock Market Reaches New High", "links": [] } ] } ``` ### Example Response with `resolve_links=true` ```json { "feed": { "title": "stock market - Google News", "...": "..." }, "entries": [ { "title": "Stock Market Reaches New High - Financial Times", "link": "https://www.ft.com/content/stock-market-reaches-new-high", "google_link": "https://news.google.com/rss/articles/CBMi...?oc=5", "link_resolved": true, "published": "Mon, 15 Jan 2024 10:00:00 GMT", "source": {"href": "https://www.ft.com", "title": "Financial Times"} } ], "links_resolved": 1 } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - The requested resource was not found - **422 Unprocessable Entity** - The request was well-formed but contains semantic errors - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/google-product # Google Product Search Amazon to retrieve product listings with pricing, ratings, and availability in JSON format. ## Endpoint ``` GET /v1/product/search/{query} ``` ## Description The Google Product Search endpoint allows you to search for products on Amazon and retrieve structured product data. The query parameter should be a URL-encoded query string. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string for product search. **Examples:** - `q=iphone+14` - `q=laptops` ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. Supported countries: `EU`, `CA`, `US`, `IE`, `GB`, `FR`, `DE`, `SE`, `IN`, `JP`, `KR`, `SG`, `AU`, `BR`. ### `X-User-Agent` (optional) **Type:** `string` Optional header to specify device type `desktop` or `mobile`. Defaults to `desktop`. **Allowed values:** `desktop`, `mobile` ## Example Requests ### cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/product/search/?q=iphone+14' \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### JavaScript ```javascript const response = await fetch('https://api.serply.io/v1/product/search/?q=iphone+14', { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Python ```python import requests url = "https://api.serply.io/v1/product/search/?q=iphone+14" headers = { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" } response = requests.get(url, headers=headers) data = response.json() print(data) ``` ## Response The response is a JSON object containing product listings from Amazon. ### Response Structure ```json { "products": [ { "link": "string", "asin": "string", "title": "string", "price": "number", "real_position": "number", "img_url": "string", "rating_stars": "string", "review_count": "number", "extras": ["string"], "bestseller": "boolean", "prime": "boolean", "is_sponsor": "boolean" } ], "ads": ["array"], "ts": "number", "device_type": "null" } ``` ### Response Fields #### `products` (array) An array of product objects, each containing: - **`link`** (string): URL to the product page on Amazon - **`asin`** (string): Amazon Standard Identification Number - **`title`** (string): Product title - **`price`** (number): Product price - **`real_position`** (number): Position of the product in search results - **`img_url`** (string): URL to the product image - **`rating_stars`** (string): Star rating (e.g., "4.3 out of 5 stars") - **`review_count`** (number): Number of reviews - **`extras`** (array of strings): Additional product information (shipping, stock status, etc.) - **`bestseller`** (boolean): Whether the product is a bestseller - **`prime`** (boolean): Whether the product is Prime eligible - **`is_sponsor`** (boolean): Whether the product is a sponsored listing #### `ads` (array) Array of advertisement objects (typically empty). #### `ts` (number) Timestamp of the response. #### `device_type` (null) Device type used for the search. ### Example Response ```json { "products": [ { "link": "https://www.amazon.com/Apple-iPhone-13-Pro-Sierra/dp/B09LPN7C8Z/ref=sr_1_2?keywords=iphone+14&qid=1668626152&sr=8-2", "asin": "B09LPN7C8Z", "title": "Apple iPhone 13 Pro, 256GB, Sierra Blue - Unlocked (Renewed)", "price": 914.97, "real_position": 3, "img_url": "https://m.media-amazon.com/images/I/51UuPZLMaCL._AC_UY218_.jpg", "rating_stars": "4.3 out of 5 stars", "review_count": 308, "extras": [ "Get it as soon as Fri, Nov 18", "FREE Shipping", "Only 10 left in stock - order soon." ], "bestseller": false, "prime": false, "is_sponsor": false } ], "ads": [], "ts": 2.0670101642608643, "device_type": null } ``` ## Status Codes - **200 OK**: Successful request - **201 Created**: Request processed successfully - **404 Not Found**: Resource not found - **422 Unprocessable Entity**: Validation error - **429 Too Many Requests**: Rate limit exceeded ## Error Handling If an error occurs, the API will return an appropriate HTTP status code with an error message in the response body. --- https://serply.io/docs/resources/google-maps # Google Maps Search Google Maps and retrieve structured place records in JSON format. ## Endpoint ``` GET /v1/maps/search/{query} ``` ## Description The Google Maps endpoint returns structured place records for a location or category search: name, address, coordinates, rating, categories, phone, timezone, and opening hours. This endpoint carries its search text differently from the rest of the API. Elsewhere the text is the `q` parameter (`/v1/search/?q=coffee+shops&num=100`). Here it is the path segment itself, URL-encoded, while `num`, `hl`, and `gl` stay ordinary query parameters: ``` https://api.serply.io/v1/maps/search/coffee%20shops%20in%20Chicago%2C%20IL?num=20&hl=en&gl=us ``` A leading `q=` inside the path segment is accepted but not required. Anything after an `&` inside the path segment is ignored, so refinements must go after the `?` to take effect. Responses are cached for 10 minutes. A cached response does not consume prepaid credits. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` The place or category to search for, URL-encoded. **Examples:** - `coffee%20shops%20in%20Chicago%2C%20IL` - `dentists%20near%20Austin%20TX` ## Query Parameters ### `num` (optional) **Type:** `integer` How many places to return. Defaults to `20`. Accepts `1` to `200`. Google often returns fewer places than requested. ### `hl` (optional) **Type:** `string` Interface language code. Defaults to `en`. ### `gl` (optional) **Type:** `string` Two-letter country code. Defaults to `us`. ## Request Headers `X-Proxy-Location` and `X-User-Agent` are **not** supported on this endpoint. It reads Google's non-JavaScript Maps transport directly rather than going through a proxy, so there is no proxy region or device to select. Use `gl` and `hl` to control locale instead. ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/maps/search/coffee%20shops%20in%20Chicago%2C%20IL?num=20&hl=en&gl=us' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### Using JavaScript/Node.js ```javascript const query = encodeURIComponent('coffee shops in Chicago, IL'); const params = new URLSearchParams({ num: '20', hl: 'en', gl: 'us' }); const response = await fetch( `https://api.serply.io/v1/maps/search/${query}?${params}`, { headers: { 'X-Api-Key': 'YOUR_API_KEY' } } ); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests from urllib.parse import quote query = quote('coffee shops in Chicago, IL') response = requests.get( f'https://api.serply.io/v1/maps/search/{query}', params={'num': 20, 'hl': 'en', 'gl': 'us'}, headers={'X-Api-Key': 'YOUR_API_KEY'} ) data = response.json() print(data) ``` ## Response The API returns a JSON object containing an array of place records. ### Response Structure ```json { "search_engine": "google_maps", "query": "coffee shops in Chicago, IL", "places": [ { "position": 1, "name": "Place Name", "data_id": "0x880e2cb109470fb1:0x1bfa35f0425ae540", "place_id": "ChIJsQ9HCbEsDogRQOVaQvA1-hs", "google_maps_url": "https://www.google.com/maps/search/?api=1&query=...", "website": "https://example.com/", "domain": "example.com", "address": "346 N Clark St Unit 4709, Chicago, IL 60654", "address_lines": ["346 N Clark St Unit 4709", "Chicago, IL 60654"], "district": "Near North Side", "latitude": 41.8887579, "longitude": -87.6312297, "rating": 4.6, "review_count": null, "review_url": null, "categories": ["Coffee shop", "Espresso bar"], "category_ids": [], "phone": null, "phone_e164": null, "timezone": "America/Chicago", "thumbnail": "https://lh3.googleusercontent.com/...", "opening_hours": { "Friday": "7 AM-5 PM" } } ], "result_count": 20, "parsed_at": "2026-08-14T20:32:17.293873Z", "metadata": { "schema": "tbm-map-positional-v1", "transport": "direct", "requested_count": 20, "language": "en", "country": "us" } } ``` ### Response Fields - **`search_engine`** (string): Always `google_maps` - **`query`** (string): The decoded search text - **`places`** (array): An array of place objects - **`position`** (number): Rank within the result set, starting at 1 - **`name`** (string): The name of the place - **`data_id`** (string): Google's internal identifier for the place - **`place_id`** (string): The Places API identifier, when available - **`google_maps_url`** (string): A link to the place on Google Maps - **`website`** (string | null): The place's own website - **`domain`** (string | null): The bare hostname of `website` - **`address`** (string | null): The full formatted address - **`address_lines`** (array[string]): The address split into display lines - **`district`** (string | null): Neighborhood or district name - **`latitude`** (number): Latitude in decimal degrees - **`longitude`** (number): Longitude in decimal degrees - **`rating`** (number | null): Average star rating out of 5 - **`review_count`** (number | null): Number of reviews - **`review_url`** (string | null): A link to the reviews listing - **`categories`** (array[string]): Human-readable category labels - **`category_ids`** (array[string]): Google category identifiers, usually empty - **`phone`** (string | null): Phone number as displayed - **`phone_e164`** (string | null): The same number in E.164 format - **`timezone`** (string | null): IANA timezone name - **`thumbnail`** (string | null): A photo URL - **`opening_hours`** (object | null): Day name to hours string - **`result_count`** (number): The number of places returned - **`parsed_at`** (string): ISO 8601 timestamp of when the response was parsed - **`metadata`** (object): Details about how the result was produced Treat every place field except `position`, `name`, `data_id`, `latitude`, and `longitude` as optional. `review_count` and `review_url` in particular are frequently `null` on broad category searches and populated on narrow ones. Values in `opening_hours` come verbatim from Google and can contain non-ASCII characters such as narrow no-break spaces and en dashes. Normalize them before display. ## Status Codes - **200 OK** - Successful response - **400 Bad Request** - Empty query, `num` outside 1-200, or a malformed `hl` or `gl` - **429 Too Many Requests** - Rate limit exceeded - **502 Bad Gateway** - Google Maps structured search is temporarily unavailable A `502` means the upstream fetch or the response parse failed. The underlying format is positional and undocumented, so a Google-side layout change surfaces as a `502` rather than as a partial result. ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/requests # Request Scrape any URL using Serply servers with automatic captcha bypass. This endpoint is perfect for extracting content from websites for AI and LLM applications. ## Endpoint ``` POST /v1/request ``` ## Description The Request endpoint allows you to scrape content from any URL using Serply's servers. This endpoint automatically bypasses most captchas and is optimized for extracting content that can be used with AI and LLM applications. You can choose to receive the content in either full HTML format or as markdown. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Request Body Parameters The request body must be JSON with the following parameters: ### `url` (required) **Type:** `string` The URL to scrape. Provide the URL as a plain string in the JSON body. **Examples:** - `https://serply.io/` - `https://serply.io/pricing` - `https://news.ycombinator.com/item?id=12345678` ### `response_type` (required) **Type:** `string` The format of the response content. **Allowed values:** - `"full"` - Returns the full HTML content of the page - `"markdown"` - Returns the content converted to markdown format (ideal for AI/LLM processing) ## Request Examples ### Using cURL #### Full Response Type ```bash curl --request POST \ --url 'https://api.serply.io/v1/request' \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "url": "https://serply.io/", "response_type": "full" }' ``` #### Markdown Response Type ```bash curl --request POST \ --url 'https://api.serply.io/v1/request' \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "url": "https://serply.io/", "response_type": "markdown" }' ``` ### Using JavaScript/Node.js #### Full Response Type ```javascript const response = await fetch('https://api.serply.io/v1/request', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ url: 'https://serply.io/', response_type: 'full' }) }); const data = await response.json(); console.log(data.data); // the raw HTML ``` #### Markdown Response Type ```javascript const response = await fetch('https://api.serply.io/v1/request', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' }, body: JSON.stringify({ url: 'https://serply.io/', response_type: 'markdown' }) }); const markdown = await response.text(); // plain markdown, not JSON console.log(markdown); ``` ### Using Python #### Full Response Type ```python import requests url = "https://api.serply.io/v1/request" headers = { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" } payload = { "url": "https://serply.io/", "response_type": "full" } response = requests.post(url, headers=headers, json=payload) html = response.json()["data"] print(html) ``` #### Markdown Response Type ```python import requests url = "https://api.serply.io/v1/request" headers = { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" } payload = { "url": "https://serply.io/", "response_type": "markdown" } response = requests.post(url, headers=headers, json=payload) markdown = response.text # plain markdown, not JSON print(markdown) ``` ## Response The two `response_type` values return different shapes -- `full` is JSON, `markdown` is plain text. ### Response Structure #### Full Response Type Returns a JSON object with the raw HTML in `data`, alongside the upstream response metadata: ```json { "data": "...", "status": 200, "headers": { "content-type": "text/html; charset=utf-8" }, "config": { "url": "https://serply.io/", "method": "GET" } } ``` #### Markdown Response Type Returns the markdown-converted content directly as the response body -- not wrapped in JSON: ``` Content-Type: text/html; charset=utf-8 # Article Title Article content in markdown format... ``` ### Response Fields - **Full**: a JSON object whose **`data`** field (string) holds the scraped page's raw HTML, plus **`status`** (the upstream HTTP status), **`headers`** (the upstream response headers), and **`config`** (the resolved request that was sent). - **Markdown**: the response body itself is the markdown text -- there is no `content`/`url`/`response_type` wrapper. Read it as plain text, not JSON. ### Example Response (Markdown) ``` Hacker News | [new](newest) | [past](front) | [comments](newcomments) 1. [Show HN: Example](https://example.com/) (example.com) 152 points by user 3 hours ago | 76 comments ``` ## Status Codes - **200 OK** - Successful response - **400 Bad Request** - Invalid parameters (missing or invalid URL, invalid response_type) - **401 Unauthorized** - Invalid or missing API key - **404 Not Found** - The requested URL could not be accessed - **405 Method Not Allowed** - The request used `GET`; this endpoint requires `POST` - **429 Too Many Requests** - Rate limit exceeded ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/docs/resources/ebay # eBay Search Search eBay to retrieve listing results with pricing, condition, seller, and shipping data in JSON format. ## Endpoint ``` GET /v1/ebay/search/{query} ``` ## Description The eBay Search endpoint allows you to search eBay listings and retrieve structured result data. The query parameter should be a URL-encoded query string. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `query` (required) **Type:** `string` A URL-encoded query string for the eBay search. **Examples:** - `q=vinyl+records` - `q=iphone+15+case` ## Request Headers ### `X-Proxy-Location` (optional) **Type:** `string` Specify the proxy location for the search. Supported countries: `EU`, `CA`, `US`, `IE`, `GB`, `FR`, `DE`, `SE`, `IN`, `JP`, `KR`, `SG`, `AU`, `BR`. ### `X-User-Agent` (optional) **Type:** `string` Optional header to specify device type `desktop` or `mobile`. Defaults to `desktop`. **Allowed values:** `desktop`, `mobile` ## Example Requests ### cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/ebay/search/?q=vinyl+records' \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### JavaScript ```javascript const response = await fetch('https://api.serply.io/v1/ebay/search/?q=vinyl+records', { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'YOUR_API_KEY' } }); const data = await response.json(); console.log(data); ``` ### Python ```python import requests url = "https://api.serply.io/v1/ebay/search/?q=vinyl+records" headers = { "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" } response = requests.get(url, headers=headers) data = response.json() print(data) ``` ## Response The response is a JSON object containing organic listing results from eBay. ### Response Structure ```json { "results": [ { "title": "string", "link": "string", "position": "number", "result_type": "string", "metadata": { "price": "string", "was_price": "string", "condition": "string", "image": "string", "seller": "string", "seller_feedback": "string", "attributes": ["string"] } } ], "total": "number", "query": "string", "ts": "number", "device_region": "string", "device_type": "null" } ``` ### Response Fields #### `results` (array) An array of listing objects, each containing: - **`title`** (string): Listing title - **`link`** (string): URL to the listing on eBay - **`position`** (number): Position of the listing in search results - **`result_type`** (string): Result classification, e.g. `organic` - **`metadata`** (object): Additional listing details - **`price`** (string): Current listing price, as displayed - **`was_price`** (string, optional): Pre-discount price, present only when the listing shows a markdown - **`condition`** (string): Item condition, e.g. `Pre-Owned`, `New` - **`image`** (string): URL to the listing's thumbnail image - **`seller`** (string): Seller username - **`seller_feedback`** (string): Seller feedback score and percentage, as displayed - **`attributes`** (array of strings): Additional listing details such as shipping cost, delivery estimate, item location, or `or Best Offer` #### `total` (number) Reported total match count. This field is not yet reliable and currently returns `0` regardless of the actual number of matching listings — use the length of `results` for a real count. #### `query` (string) The search query that was run. #### `ts` (number) Time in seconds the request took to complete. #### `device_region` (string) Proxy region used for the request, if specified via `X-Proxy-Location`. #### `device_type` (null) Device type used for the search. ### Example Response ```json { "results": [ { "title": "Prince - Welcome 2 America 12” Vinyl Album 2x Records With One Etched Side", "link": "https://www.ebay.com/itm/267755938013?epid=...", "position": 1, "result_type": "organic", "metadata": { "price": "$27.06", "was_price": "$27.00", "condition": "Pre-Owned", "image": "https://i.ebayimg.com/images/g/.../s-l500.webp", "seller": "dshvinyl", "seller_feedback": "100% positive (1.2K)", "attributes": [ "or Best Offer", "+$4.94 delivery in 2-3 days", "Located in United Kingdom" ] } } ], "total": 0, "query": "vinyl records", "ts": 2.53, "device_region": "", "device_type": null } ``` ## Status Codes - **200 OK**: Successful request - **201 Created**: Request processed successfully - **404 Not Found**: Resource not found - **422 Unprocessable Entity**: Validation error - **429 Too Many Requests**: Rate limit exceeded ## Error Handling If an error occurs, the API will return an appropriate HTTP status code with an error message in the response body. --- https://serply.io/docs/resources/reddit # Reddit Retrieve subreddit listings, subreddit metadata, a user's post history, a single post, or a comment thread, all in Reddit's own JSON shape. ## Endpoints ``` GET /v1/reddit/subreddit/{subreddit} GET /v1/reddit/subreddit/{subreddit}/about GET /v1/reddit/user/{username} GET /v1/reddit/post/{id} GET /v1/reddit/comments/{id} ``` ## Description Reddit addresses a subreddit or a post by id, so the identifier is a real path segment rather than a `q` parameter. The options are ordinary query parameters: ``` https://api.serply.io/v1/reddit/subreddit/python?limit=10&sort=hot ``` Responses are cached for 10 minutes. A cached response does not consume prepaid credits. ## Authentication All requests require authentication using the `X-Api-Key` header. See the [Authentication guide](/docs/guides/authentication) for more details. ## Path Parameters ### `subreddit` (required for the subreddit endpoints) **Type:** `string` A subreddit name, without the `r/` prefix. **Examples:** `python`, `AskReddit` ### `username` (required for the user endpoint) **Type:** `string` A Reddit username, without the `u/` prefix. **Example:** `spez` ### `id` (required for the post and comments endpoints) **Type:** `string` A Reddit post ID (the same ID that appears in the post's URL). **Example:** `1vfemi1` ## Query Parameters `subreddit` and `user` accept `limit`, `sort`, `t`, and `after`. `comments` accepts only `sort`. `post` accepts `sort` and `with_comments`. `subreddit/{subreddit}/about` accepts none. ### `limit` (optional) **Type:** `integer` How many items to return. Defaults to `25`. Accepts `1` to `100`. ### `sort` (optional) **Type:** `string` Sort order. Defaults to `hot` for listings and `confidence` for comment threads. **Common values:** `hot`, `new`, `top`, `confidence` ### `t` (optional) **Type:** `string` Time window, used together with a `top`-style sort. Defaults to `all`. **Allowed values:** `hour`, `day`, `week`, `month`, `year`, `all` ### `after` (optional) **Type:** `string` Pagination cursor. Pass the previous response's `data.after` to fetch the next page. ### `with_comments` (optional, `post` only) **Type:** `boolean` Adds the post's comment tree to the response under a `comments` key. Defaults to `false`. ## Request Example ### Using cURL ```bash curl --request GET \ --url 'https://api.serply.io/v1/reddit/subreddit/python?limit=10&sort=hot' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ### Using JavaScript/Node.js ```javascript const response = await fetch( 'https://api.serply.io/v1/reddit/subreddit/python?limit=10&sort=hot', { headers: { 'X-Api-Key': 'YOUR_API_KEY' } } ); const data = await response.json(); console.log(data); ``` ### Using Python ```python import requests response = requests.get( 'https://api.serply.io/v1/reddit/subreddit/python', params={'limit': 10, 'sort': 'hot'}, headers={'X-Api-Key': 'YOUR_API_KEY'} ) data = response.json() print(data) ``` ## Response The response is Reddit's own listing shape, untouched, so anything that already knows how to read a Reddit listing needs no translation layer. ### Response Structure ```json { "kind": "Listing", "data": { "after": "t3_1vpk70t", "children": [ { "kind": "t3", "data": { "title": "string", "subreddit_name_prefixed": "string", "selftext": "string", "score": "number", "num_comments": "number" } } ] } } ``` ### Response Fields - **`kind`** (string): Reddit's type tag for the top-level object, e.g. `Listing` - **`data`** (object): The listing payload - **`after`** (string | null): Pagination cursor for the next page, or `null` on the last page - **`children`** (array): The listing items, each Reddit's own `kind`/`data` shape (e.g. `t3` for a post, `t1` for a comment) `subreddit/{subreddit}/about` returns a single object (not a listing) describing the subreddit itself. `comments/{id}` is the one endpoint that does not return Reddit's shape at the top level. It returns an envelope, `{ "cached": boolean, "data": [...] }`, where `data` holds two listings: the post first, then its comment tree. Read the thread from `data[1].data.children`, where each child is a `t1` comment. ```json { "cached": true, "data": [ { "kind": "Listing", "data": { "children": [ { "kind": "t3", "data": { "title": "..." } } ] } }, { "kind": "Listing", "data": { "children": [ { "kind": "t1", "data": { "body": "..." } } ] } } ] } ``` ### Reading a single post `post/{id}` is the same upstream call as `comments/{id}` with the unwrapping done for you: it returns the post object itself — no `Listing` envelope, no array — so the body is at `selftext` rather than `data[0].data.children[0].data.selftext`. ```bash curl --request GET \ --url 'https://api.serply.io/v1/reddit/post/1ul97v0' \ --header 'X-Api-Key: YOUR_API_KEY' ``` ```json { "id": "1ul97v0", "title": "What should an open-source browser for AI agents actually solve?", "subreddit_name_prefixed": "r/AI_Agents", "author": "championscalc", "selftext": "I'm thinking about starting an open-source project for AI agents...", "selftext_html": "<!-- SC_OFF --><div class=\"md\">...", "url": "https://www.reddit.com/r/AI_Agents/comments/1ul97v0/...", "permalink": "/r/AI_Agents/comments/1ul97v0/what_should_an_opensource_browser_for_ai_agents/", "score": 2, "num_comments": 18, "created_utc": 1782970036.0 } ``` `selftext` is the post body as the author wrote it, in Reddit's markdown; `selftext_html` is the same body pre-rendered. A link post has an empty `selftext` — its content is the `url` it points at. Add `?with_comments=true` to get the comment tree alongside the post, under a `comments` key holding the same `t1` children `comments/{id}` returns. ### Example Response ```json { "kind": "Listing", "data": { "after": "t3_1vpk70t", "children": [ { "kind": "t3", "data": { "title": "Showcase Thread", "subreddit_name_prefixed": "r/Python", "selftext": "Post all of your code/projects/showcases here...", "score": 42, "num_comments": 118 } } ] } } ``` ## Status Codes - **200 OK** - Successful response - **404 Not Found** - No post exists with that ID (`post` endpoint only) - **429 Too Many Requests** - Rate limit exceeded - **502 Bad Gateway** - The Reddit proxy is temporarily unavailable ## Error Responses See the [Errors guide](/docs/guides/errors) for information on error response formats. --- https://serply.io/mcp # Serply MCP Server Serply hosts a remote [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server at `https://api.serply.io/mcp`. Point Claude Code, Claude Desktop, Cursor, or any other MCP client at it with your API key and the agent gets fourteen tools for searching the live web - Google, Bing, News, Scholar, Video, Jobs, Maps, product listings, and Reddit - plus a scraper that returns any public page as markdown. There is nothing to install, deploy, or keep running: the server is hosted, and a tool call bills against the same credits as the [REST API](/docs). ## Server address and authentication | | | |---|---| | **Endpoint** | `https://api.serply.io/mcp` | | **Transport** | Streamable HTTP | | **Auth** | `X-Api-Key` header | Get an API key at [app.serply.io/users/sign_up](https://app.serply.io/users/sign_up) - new accounts include 2,500 free credits, no card required. The same key works for the REST API and the MCP server interchangeably. See the [Authentication guide](/docs/guides/authentication) for key handling best practices. ## Connect Claude Code One command: ```bash claude mcp add --transport http serply https://api.serply.io/mcp \ --header "X-Api-Key: YOUR_API_KEY" ``` That is the whole install. Then just ask - "what shipped in the httpx changelog this month?" - and Claude picks the right tool. ## Connect Claude Desktop Claude Desktop's config file does not pass custom headers to remote servers, so bridge through [mcp-remote](https://www.npmjs.com/package/mcp-remote), which runs the HTTP connection behind a local stdio server. Add this to `claude_desktop_config.json` (on macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`): ```json { "mcpServers": { "serply": { "command": "npx", "args": [ "-y", "mcp-remote", "https://api.serply.io/mcp", "--header", "X-Api-Key:YOUR_API_KEY" ] } } } ``` Restart Claude Desktop and the fourteen tools appear in the tools menu. ## Connect Cursor Cursor speaks streamable HTTP directly. Add to `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` for every project): ```json { "mcpServers": { "serply": { "url": "https://api.serply.io/mcp", "headers": { "X-Api-Key": "YOUR_API_KEY" } } } } ``` ## Connect any other MCP client Windsurf, Cline, Zed, and anything else that speaks stdio can use the same `mcp-remote` bridge shown for Claude Desktop above - the JSON block is identical, only the config file location changes. Clients with native remote-MCP support connect to `https://api.serply.io/mcp` directly with the `X-Api-Key` header. ## The fourteen tools Every tool name and parameter below is read off the live server via `tools/list`. | Tool | Parameters | What it returns | |---|---|---| | `google_search` | query, num, start, proxy_location, device | Organic Google results - the default reach for anything factual or recent | | `bing_search` | query, proxy_location, device | Bing organic results, plus text ads and shopping ads Google does not return | | `google_news_search` | query, ceid, proxy_location, device | News coverage with publisher and date; `ceid` picks the country edition | | `google_video_search` | query, num, proxy_location, device | Video results - tutorials, demos, news clips | | `google_scholar_search` | query, num, proxy_location, device | Peer-reviewed papers with authors and citation counts | | `google_jobs_search` | query, proxy_location, device | Postings from Google's jobs index, which aggregates LinkedIn and Indeed | | `google_maps_search` | query, num, hl, gl | Local businesses with address, coordinates, rating, phone, and hours | | `amazon_product_search` | query, proxy_location, device | Product listings with prices and availability | | `scrape_url` | url, response_type | Any public page as markdown or raw HTML | | `reddit_subreddit_posts` | subreddit, limit, sort, t, after | A subreddit's post listing | | `reddit_subreddit_about` | subreddit | Subscriber count, description, and rules | | `reddit_user_posts` | username, limit, sort, t, after | One account's post history | | `reddit_post` | post_id, with_comments, sort, max_depth | A single post with its body; add `with_comments` for the thread underneath | | `reddit_post_comments` | post_id, sort, max_depth | The comment tree with scores, nested to the depth you ask for | ## What the tools return Every tool except `google_maps_search` returns rendered markdown rather than JSON - readable by the model as-is, with no parsing step spending context on brackets. Maps is the exception twice over: it returns a structured object with a `places[]` array, and it takes `hl`/`gl` locale parameters instead of `proxy_location`/`device`. The Reddit tools take names and ids in whatever form you already have: `r/python` or `python`, `u/spez` or `spez`, `t3_1vfemi1` or `1vfemi1`. A post id that does not exist comes back as a plain "No post found" answer, not a tool error, so an agent should not retry it. ## Credits, caching, and errors A tool call is one API request: 1 credit per successful, uncached request, billed to the same balance as the REST API. Most endpoints cache for several minutes, and cached responses cost nothing - so an agent retrying the same query during a session is free. `429` means the rate limit was hit; back off and retry. `502` means the upstream fetch or parse failed - it is transient, so retry once before giving up. The full error-response reference is in the [Errors guide](/docs/guides/errors). ## FAQ ### Do I need to run or deploy anything? No. The server is hosted at `https://api.serply.io/mcp`. Registering it is one config entry; there is no process to keep alive and nothing to update. ### Which MCP clients work with it? Any client that implements MCP. Clients with remote-server support (Claude Code, Cursor) connect directly over streamable HTTP; stdio-only clients (Claude Desktop, Windsurf, Cline) connect through the `mcp-remote` bridge shown above. ### What does a tool call cost? The same as the equivalent REST call: 1 credit per successful, uncached request. New accounts get 2,500 free credits with no card. See [pricing](/pricing) for plans beyond that. ### Can I build my own MCP server on the Serply API instead? Yes - the REST API is fully documented and an MCP wrapper is about thirty lines. There is a working walkthrough in [Build an MCP Server for Serply Search, News, and Web Scraping](/blog/mcp-server-serply-search-api). The hosted server exists so you do not have to. ## Related - [Agent Skill](/docs/guides/agent-skill) - a `SKILL.md` that teaches Claude and other Skill-compatible agents both the REST API and this MCP server, installable with `npx skills add serply-inc/skills` - [API Endpoints](/docs) - the full REST reference behind each tool - [Strands Agents](/docs/guides/strands) - load the fourteen tools into an AWS Strands agent with `MCPClient(url=..., headers=...)` - [MCP resources vs tools for search](/blog/mcp-resources-vs-tools-search) and [a TypeScript MCP server on the Serply API](/blog/mcp-server-typescript-search-tools) from the blog