Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Searlo Rankings API
description: Live search results as clean JSON. The search endpoints return Google results — web, images, news, video, shopping, maps, scholar, patents and the full results page with ads, People Also Ask and AI Overviews — plus AI answers (ChatGPT, Copilot, Gemini), rank checks, web data (scrape, crawl, map, extract) and account endpoints. Get a free API key with 3,000 credits (no card required) at https://dashboard.searlo.tech. Not affiliated with or endorsed by Google LLC or any other search engine. All trademarks belong to their owners.
version: 1.2.0
contact:
name: Searlo
url: https://searlo.tech
email: hello@searlo.tech
license:
name: Proprietary API — see https://searlo.tech/terms
servers:
- url: https://api.searlo.tech/api/v1
description: Production
security:
- ApiKeyAuth: []
- BearerAuth: []
tags:
- name: Rankings
description: Where a domain ranks for a keyword, from a live SERP read, with stored history.
paths:
/rankings/check:
post:
tags:
- Rankings
summary: Where a domain ranks for a keyword
description: 'A live SERP read to the requested depth. Billed like the SERP it reads: 3 credits per 10 results of depth (depth 10 = 3 credits, depth 100 = 30 credits). The domain is reduced to its registrable domain, so any subdomain counts as a match. Every check is stored for /rankings/history.'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- keyword
- domain
properties:
keyword:
type: string
minLength: 1
maxLength: 400
domain:
type: string
minLength: 3
maxLength: 253
gl:
type: string
minLength: 2
maxLength: 2
default: us
hl:
type: string
minLength: 2
maxLength: 5
default: en
device:
type: string
enum:
- desktop
- mobile
default: desktop
location:
type: string
maxLength: 300
description: A country, by name, two-letter code or uule, applied as gl and echoed in the result. A city or state returns 400 CITY_LOCATION_UNAVAILABLE before any credit is taken.
depth:
type: integer
minimum: 10
maximum: 100
default: 100
cache:
type: boolean
default: true
responses:
'200':
description: Position, ranking URL and the depth searched
content:
application/json:
schema:
type: object
properties:
schema:
type: string
example: ranking-check.v1
result:
$ref: '#/components/schemas/RankCheckResult'
credits:
type: object
properties:
used:
type: number
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'429':
$ref: '#/components/responses/RateLimited'
'503':
$ref: '#/components/responses/SerpUnavailable'
operationId: postRankingsCheck
x-operation-id-source: derived
/rankings/batch:
post:
tags:
- Rankings
summary: Rank checks for up to 20 keywords and one domain
description: One check per distinct keyword, at the same price per keyword as /rankings/check. The response is 200 whenever the batch ran, so a keyword that fails inside it is still billed.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- keywords
- domain
properties:
keywords:
type: array
minItems: 1
maxItems: 20
items:
type: string
minLength: 1
maxLength: 400
domain:
type: string
minLength: 3
maxLength: 253
gl:
type: string
minLength: 2
maxLength: 2
default: us
hl:
type: string
minLength: 2
maxLength: 5
default: en
device:
type: string
enum:
- desktop
- mobile
default: desktop
location:
type: string
maxLength: 300
description: As on /rankings/check. A city or state refuses the whole batch with 400 CITY_LOCATION_UNAVAILABLE before any credit is taken.
depth:
type: integer
minimum: 10
maximum: 100
default: 100
cache:
type: boolean
default: true
responses:
'200':
description: One row per distinct keyword
content:
application/json:
schema:
type: object
properties:
schema:
type: string
example: ranking-batch.v1
results:
type: array
items:
type: object
description: success plus the check result fields, or keyword, success false, code, retryable and message.
additionalProperties: true
credits:
type: object
properties:
used:
type: number
meta:
type: object
description: domain, requested, distinct, measured, ranking, failed and max_sync_batch.
additionalProperties: true
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'402':
$ref: '#/components/responses/InsufficientCredits'
'429':
$ref: '#/components/responses/RateLimited'
operationId: postRankingsBatch
x-operation-id-source: derived
/rankings/history:
get:
tags:
- Rankings
summary: Stored rank checks for a domain (free)
description: The checks you ran, newest first. It is not a back-filled index. With `keyword` set and at least two checks, `change` gives the movement between the latest two.
parameters:
- name: domain
in: query
required: true
schema:
type: string
minLength: 3
maxLength: 253
- name: keyword
in: query
schema:
type: string
minLength: 1
maxLength: 400
- name: gl
in: query
schema:
type: string
minLength: 2
maxLength: 2
- name: device
in: query
schema:
type: string
enum:
- desktop
- mobile
- name: from
in: query
schema:
type: string
format: date-time
- name: to
in: query
schema:
type: string
format: date-time
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 1000
default: 100
responses:
'200':
description: Rank snapshots, newest first
content:
application/json:
schema:
type: object
properties:
schema:
type: string
example: ranking-history.v1
series:
type: array
items:
type: object
properties:
keyword:
type: string
domain:
type: string
gl:
type: string
hl:
type: string
device:
type: string
position:
type:
- integer
- 'null'
url:
type:
- string
- 'null'
search_depth:
type: integer
serp_features:
type: array
items:
type: string
checked_at:
type: string
format: date-time
change:
type:
- object
- 'null'
description: Positive delta = moved up. event is entered or dropped_out when one side is null.
properties:
delta:
type:
- integer
- 'null'
from:
type:
- integer
- 'null'
to:
type:
- integer
- 'null'
event:
type: string
enum:
- entered
- dropped_out
credits:
type: object
properties:
used:
type: number
meta:
type: object
additionalProperties: true
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
operationId: getRankingsHistory
x-operation-id-source: derived
components:
responses:
RateLimited:
description: 'The account''s rate limit (code 4201), the API key''s own (code 4202) or the cap on searches in flight (code 4203, errorCode CONCURRENCY_LIMIT, Retry-After 1, X-Concurrency-Limit carries the cap). Wait Retry-After seconds. See also AT_CAPACITY / BUSY / VERIFIER_BUSY, a full lane rather than a limit reached. Never charged.
'
headers:
Retry-After:
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
Unauthorized:
description: Missing, invalid or expired API key (code UNAUTHORIZED)
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
BadRequest:
description: 'Invalid request (code VALIDATION_ERROR; errors lists the fields). Search endpoints also answer 400 PAGE_OUT_OF_RANGE (maxPage) and DATE_FILTER_UNAVAILABLE, with the name in errorCode. Never charged.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
InsufficientCredits:
description: 'Account balance too low to complete the request (code INSUFFICIENT_CREDITS). details gives required, available and shortfall; topUpUrl links to buying more.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
SerpUnavailable:
description: 'code UPSTREAM_UNAVAILABLE: the results page came back unusable (retryable true, with a short Retry-After). Refunded. A switched-off endpoint answers 403 FEATURE_NOT_AVAILABLE instead, and an operator query (site:, intitle:, ...) 422 QUERY_NOT_SERVED; neither is charged.
'
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
schemas:
RankCheckResult:
type: object
properties:
keyword:
type: string
domain:
type: string
description: The registrable domain matched.
position:
type:
- integer
- 'null'
url:
type:
- string
- 'null'
title:
type:
- string
- 'null'
found:
type: boolean
search_depth:
type: integer
description: The depth requested, which is what gives found=false its meaning.
organic_count:
type: integer
pages_fetched:
type: integer
gl:
type: string
hl:
type: string
device:
type: string
serp_features:
type: array
items:
type: string
competitors_above:
type: array
maxItems: 20
items:
type: string
checked_at:
type: string
format: date-time
cached:
type: boolean
ErrorResponse:
type: object
properties:
success:
type: boolean
example: false
code:
description: 'Stable machine-readable name of the failure. Strings: VALIDATION_ERROR, INVALID_JSON, INVALID_IDEMPOTENCY_KEY, CITY_LOCATION_UNAVAILABLE, UNKNOWN_LOCATION, INVALID_DOMAIN, UNAUTHORIZED, INSUFFICIENT_CREDITS, FORBIDDEN, FEATURE_NOT_AVAILABLE, API_KEY_SCOPE_FORBIDDEN, NOT_FOUND, IDEMPOTENCY_REQUEST_IN_PROGRESS, PAYLOAD_TOO_LARGE, QUERY_NOT_SERVED, COUNTRY_UNAVAILABLE, IDEMPOTENCY_KEY_REUSED, TARGET_NOT_ALLOWED, AT_CAPACITY, BUSY, VERIFIER_BUSY, INTERNAL_ERROR, UPSTREAM_UNAVAILABLE, UPSTREAM_RATE_LIMITED, WARMING, UPSTREAM_TIMEOUT, CREDIT_SYSTEM_UNAVAILABLE, REVIEWS_WITHHELD, SERVICE_UNAVAILABLE, UPSTREAM_FAILED. The rate-limit 429s use numbers: 4201 (account), 4202 (API key), 4203 (searches in flight). Some 4xx carry their name in errorCode instead (see errorCode). No error is charged.
'
oneOf:
- type: string
- type: integer
example: CITY_LOCATION_UNAVAILABLE
message:
type: string
description: For people; may be reworded. Branch on code.
retryable:
type: boolean
description: Whether the same request can succeed later.
retryAfterSec:
type: integer
description: Seconds to wait before a retry (also the Retry-After header).
errors:
type: array
description: Validation errors, one per field.
items:
type: object
properties:
field:
type: string
message:
type: string
suggestions:
type: array
items:
type: string
description: 'UNKNOWN_LOCATION: close matches.'
location:
type: string
description: 'CITY_LOCATION_UNAVAILABLE: the place the request named.'
errorCode:
type: string
description: 'The name on errors that carry it here instead of in code, so read code and fall back to errorCode: PAGE_OUT_OF_RANGE (with maxPage), DATE_FILTER_UNAVAILABLE, INVALID_REQUEST, PLACE_NOT_FOUND, TARGET_REFUSED (with targetStatus), TARGET_UNREACHABLE (with targetError), INSUFFICIENT_CREDITS on POST /extract. Also set with code on 4203 (CONCURRENCY_LIMIT), FEATURE_NOT_AVAILABLE, TARGET_NOT_ALLOWED, REVIEWS_WITHHELD and every 503.
'
retryAfter:
type: integer
description: Seconds to wait, on 429 4201 to 4203 and on a retryable 503.
maxPage:
type: integer
description: 'PAGE_OUT_OF_RANGE: the last page that can be served at this limit.'
targetStatus:
type: integer
description: 'TARGET_REFUSED: the site''s own HTTP status.'
targetError:
type: string
description: 'TARGET_UNREACHABLE: why the site could not be fetched.'
details:
type: object
description: 'INSUFFICIENT_CREDITS: required, available and shortfall.'
additionalProperties:
type: number
topUpUrl:
type: string
description: 'INSUFFICIENT_CREDITS: a link to buy more credits.'
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: x-api-key
description: 'API key from your Searlo dashboard, e.g. sk_.... Get a free key with 3,000 credits at https://dashboard.searlo.tech (no card required).
'
BearerAuth:
type: http
scheme: bearer
description: 'The same API key, sent as Authorization: Bearer sk_.... A bearer token that does not start with sk_ is read as a dashboard login token.
'
CseKeyParam:
type: apiKey
in: query
name: key
description: 'The Searlo API key as the key query parameter, where Google''s Custom Search JSON API takes its key. Used by /customsearch/v1 only.
'
externalDocs:
description: Full documentation
url: https://searlo.tech/docs