# 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