{
"status": "completed",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"progress": {
"message": "Search completed successfully",
"progress": 1
},
"result": {
"success": true,
"data": {
"web": [
{
"title": "Best Espresso Machines of 2026",
"url": "https://example.com/best-espresso-machines-2026",
"description": "Our picks for the best espresso machines this year, tested and compared.",
"position": 1
}
]
},
"stats": {
"durationMs": 1980
}
},
"error": null
}{
"status": "queued",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"message": "Search job has been queued. Poll /api/search/550e8400-e29b-41d4-a716-446655440000 to get the result."
}{
"status": "error",
"message": "You have exceeded your monthly credit limit."
}{
"status": "error",
"message": "Request validation failed. Fix the errors below and try again.",
"errors": [
"\"query\" is required and must be a non-empty string."
]
}Submit a Search
Run a real search query and get back web, news, images, videos, research, and developer results as structured data
{
"status": "completed",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"progress": {
"message": "Search completed successfully",
"progress": 1
},
"result": {
"success": true,
"data": {
"web": [
{
"title": "Best Espresso Machines of 2026",
"url": "https://example.com/best-espresso-machines-2026",
"description": "Our picks for the best espresso machines this year, tested and compared.",
"position": 1
}
]
},
"stats": {
"durationMs": 1980
}
},
"error": null
}{
"status": "queued",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"message": "Search job has been queued. Poll /api/search/550e8400-e29b-41d4-a716-446655440000 to get the result."
}{
"status": "error",
"message": "You have exceeded your monthly credit limit."
}{
"status": "error",
"message": "Request validation failed. Fix the errors below and try again.",
"errors": [
"\"query\" is required and must be a non-empty string."
]
}jobId back, then poll GET /search/{jobId} until status is completed. Most searches resolve in a few seconds, and if you’d rather not poll at all, wait: true returns the result in this same call.
Working with an AI assistant instead of writing code? The MCP server exposes this endpoint as the spidra_search tool.
By default a search only returns web results. Request more with the sources array:
{
"query": "best espresso machine 2026",
"sources": ["web", "news", "images"]
}
| Source | Description |
|---|---|
web | Standard web results (default) |
news | News articles |
images | Image results |
videos | Video results |
research | Academic papers from arXiv, PubMed, bioRxiv, and medRxiv |
developer | GitHub issues and pull requests |
Geo-Targeting
Passcountry to localize results to a specific region. This is usually a 2-letter country code, or global, eu, asia:
{
"query": "top restaurants",
"country": "fr"
}
Domain Filtering
Restrictweb results to specific domains, or keep specific domains out. You can pass one or the other, but never both in the same request.
With the following example, you can include only results from reddit.com:
{
"query": "espresso machine reviews",
"includeDomains": ["reddit.com"]
}
excludeDomains instead:
{
"query": "espresso machine reviews",
"excludeDomains": ["pinterest.com"]
}
includeDomains, excludeDomains, and filetype only apply to web/news/images/videos. They’re not supported on research or developer, covered below.Research and Developer Sources
Theresearch source searches academic papers and returns authors and doi alongside the usual fields:
{
"query": "transformer attention mechanisms",
"sources": ["research"]
}
developer source searches GitHub issues and pull requests. Write the query the way you’d type it into GitHub’s own search, qualifiers and all:
{
"query": "memory leak is:issue repo:vercel/next.js",
"sources": ["developer"]
}
PDF Search
Passfiletype: "pdf" to restrict web results to PDF files:
{
"query": "annual report 2026",
"filetype": "pdf"
}
scrapeOptions to fetch each PDF’s actual content as markdown in the same request. See Scrape Content From Results and Document URLs below.HD Image Search With Size Filtering
Forimages results, add imagesize: or larger: straight into your query. It works the same way includeDomains/excludeDomains do above, just written inline instead of as a separate field.
Here is an example using the imagesize: filter:
curl -X POST https://api.spidra.io/api/search \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"query": "sunset imagesize:1920x1080",
"sources": ["images"],
"limit": 5
}'
larger: filter:
curl -X POST https://api.spidra.io/api/search \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"query": "mountain wallpaper larger:1920x1080",
"sources": ["images"],
"limit": 8
}'
imagesize:1920x1080: Full HD (1080p), exactimagesize:2560x1440: QHD (1440p), exactimagesize:3840x2160: 4K UHD, exactlarger:1920x1080: HD and abovelarger:2560x1440: QHD and above
Scrape Content From Results
By default, Search returns metadata, but you can addscrapeOptions to also fetch each web result’s actual page content, in the same request:
{
"query": "posthog before_send config",
"scrapeOptions": {
"formats": ["markdown"]
}
}
completed. markdown lands directly on each result, the same call, no second request:
{
"data": {
"web": [
{
"title": "How to configure before_send",
"url": "https://posthog.com/tutorials/...",
"position": 1,
"markdown": "# How to configure before_send\n\n..."
}
]
}
}
scrapeOptions off and use POST /scrape on whichever URLs you actually want.Result Limit
Control how many results come back per source (1-20, default 10):{
"query": "electric cars",
"sources": ["web", "images"],
"limit": 20
}
Pagination
A completed search includes anextPageTokens object with one opaque token per source that returned results. Pass it back as pageTokens on your next request to reliably continue where that source left off:
{
"query": "espresso machine reviews",
"pageTokens": {
"web": "eyJwYWdlIjoyfQ"
}
}
data.nextPageTokens.web, not something you construct yourself:
{
"data": {
"web": [ /* ... */ ],
"nextPageTokens": {
"web": "eyJwYWdlIjoyfQ"
}
}
}
page number instead:
{
"query": "espresso machine reviews",
"page": 2
}
pageTokens is more reliable than a plain page number since it continues from the exact result set your last response returned. A source without a token, or with no more pages left, is simply absent from nextPageTokens.Tokens are sealed values that only Spidra can read. Pass them back exactly as you received them. A token that has been changed, or that is too old to resume, is not an error: that source just runs as a fresh search.position also behaves differently between the two. With pageTokens, positions continue across pages (11, 12, 13 on page 2). With a plain page number, each page numbers its results from 1.Time Range
PasstimeRange to restrict results to a recency window: hour, day, week, month, or year. This is especially useful for news, where older results usually matter less:
{
"query": "product launch",
"sources": ["news"],
"timeRange": "week"
}
Get the Result Without Polling
For a plain search withoutscrapeOptions, pass wait: true to hold the request open and get the result back in this same call:
{
"query": "best espresso machine 2026",
"wait": true
}
status: "completed" and the real result already attached, the same shape GET /search/{jobId} returns once a polled job finishes:
{
"status": "completed",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"progress": { "message": "Search completed successfully", "progress": 1 },
"result": {
"success": true,
"data": { "web": [ /* ... */ ] },
"stats": { "durationMs": 1980 }
},
"error": null
}
202 and jobId you’d get without wait, and you poll GET /search/{jobId} from there. wait is ignored whenever scrapeOptions is set, since that case can legitimately take much longer than any request should stay open.Extras
When available for the query,web results come with an extras object alongside them: related searches, a trending news carousel, or a direct answer box such as a currency conversion.
{
"data": {
"web": [ /* ... */ ],
"extras": {
"answerBox": { "type": "currency", "value": "17.43 Euro" },
"relatedSearches": ["20 usd to gbp", "usd to eur exchange rate today"]
}
}
}
extras is only present when Spidra actually found one of these for that specific query. It’s never guaranteed, and never present for news/images/videos.Check Job Status
View Logs
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
The search query
2000Which result types to fetch. research returns academic papers, developer returns GitHub issues and PRs.
web, news, images, videos, research, developer Results per source
1 <= x <= 201-indexed page number. Prefer pageTokens once you have one, it's a more reliable way to continue to the next page than a bare page number.
x >= 1One opaque token per source, taken from a prior response's nextPageTokens, for reliably continuing to that source's next page.
Show child attributes
Show child attributes
Restricts results to a recency window. Support can vary by source and query, and a window that can't be honored is simply ignored rather than erroring.
hour, day, week, month, year 2-letter country code, or 'global', 'eu', 'asia'
Only return web results from these domains (site: operator). Mutually exclusive with excludeDomains -- sending both is a validation error.
20Exclude web results from these domains (-site: operator). Mutually exclusive with includeDomains -- sending both is a validation error.
20Restricts results to PDF files. Doesn't apply to research/developer sources.
pdf Hold the request open and respond with the real result directly, instead of always returning a queued job to poll. Ignored when scrapeOptions is set. Falls back to the normal queued response if the search doesn't finish within the wait window (about 20 seconds).
Opt-in: also fetch each web result's actual page content before the job reports completed. The markdown lands directly on each enriched result, with no second job id to poll. A content-fetch search takes as long as the slowest scraped page (real scraping isn't instant), not the usual 1-8s.
Show child attributes
Show child attributes
Response
Job completed within the wait window (only possible when wait: true was sent and scrapeOptions was not set)
Was this page helpful?

