Reference

Account & Utilities

Your balance, and what this API serves right now. All free (0 credits); each needs your API key, like every other call.

Endpoints#

  • GET/credits/balance
  • GET/credit/balance
  • GET/search/capabilities
  • GET/search/countries
  • GET/search/states

Credit Balance#

GET /credits/balance returns the credits available to your account, and nothing else about it. It works with an API key (x-api-key, or Authorization: Bearer sk_...) and with a dashboard login. It is free, and like any call it counts against your request limits.

JSON
{  "success": true,  "credits": {    "available": 2500,    "reserved": 6,    "expiresAt": "2027-09-01T10:00:00.000Z",    "expiringCredits": 2500  }}
FieldDescription
availableCredits you can spend now. It is the balance GET /user/profile shows (0 while the account is on hold)
reservedPresent when known: how much of available requests still in flight, or awaiting settlement, already hold
expiresAt / expiringCreditsPresent when some credits expire: when the next ones do, and how many expire then

The answer is never cached (Cache-Control: no-store).

GET /credit/balance is the fuller view: the same balance with a low-balance flag and the expiry of each grant.

JSON
{  "statusCode": 200,  "data": {    "balance": 2500,    "available": 2500,    "isLowBalance": false,    "lastUpdated": "2026-09-29T08:12:03.000Z",    "expiry": {      "policyApplies": true,      "durationDays": 365,      "totalExpiringAmount": 2500,      "nonExpiringAmount": 0,      "nextExpiryAt": "2027-09-01T10:00:00.000Z",      "daysUntilNextExpiry": 336,      "nextExpiryAmount": 2500,      "breakdown": [        { "sourceType": "purchase", "amount": 2500, "grantedAt": "2026-09-01T10:00:00.000Z", "expiresAt": "2027-09-01T10:00:00.000Z", "daysRemaining": 336, "status": "active" }      ]    }  },  "message": "Credit balance retrieved successfully",  "success": true}
FieldDescription
availableCredits you can spend now (balance is the same number)
isLowBalanceBelow the low-balance threshold (100 credits by default)
expiry.nextExpiryAt / nextExpiryAmountWhen credits next expire, and how many expire then
expiry.breakdownEach purchase or grant still holding credits, soonest expiry first

Every metered response also carries X-Credits-Deducted and X-Credits-Remaining, so a client rarely needs to poll this.

Capabilities#

GET /search/capabilities lists every endpoint this deployment serves, what it costs, whether it is switched on, and how it has done over the last two hours. Read it instead of hard-coding which endpoints are available: a switched-off endpoint answers 403 FEATURE_NOT_AVAILABLE (not charged), and this tells you before you pay the round trip.

JSON
{  "success": true,  "generatedAt": "2026-09-29T17:20:00.000Z",  "capabilities": [    {      "id": "serp/html",      "method": "GET",      "path": "/serp/html",      "credits": 2,      "creditsNote": "per page; 8 with js=true (rendered, AI Overview opened out)",      "group": "google",      "enabled": true,      "state": "live",      "health": { "window": "2h", "ok": 412, "fail": 6, "successRate": 0.99 }    },    {      "id": "backlinks",      "method": "GET",      "path": "/backlinks",      "credits": 0,      "group": "backlinks",      "enabled": false,      "reason": "No backlink provider is bound to this deployment. The endpoints answer 501 rather than returning synthesised link data.",      "state": "off",      "health": { "window": "2h", "ok": 0, "fail": 0, "successRate": null }    }  ],  "states": { "live": "serving", "degraded": "on, but most recent calls failed", "off": "switched off here" }}

state is off when the endpoint is switched off (reason says why), degraded when at least 3 calls in the window and fewer than half of them succeeded, and live otherwise. successRate is a fraction (0–1), null with no calls in the window. The response may be cached for 30 seconds.

Countries and States#

GET /search/countries lists the countries the AI answer endpoints accept as gl. The search endpoints accept any two-letter gl, applied as the results country.

JSON
{  "success": true,  "engine": "all",  "countries": [    { "code": "US", "name": "United States", "states": true, "exit": true },    { "code": "GB", "name": "United Kingdom", "states": false, "exit": true }  ]}

GET /search/states lists the US states with Google's name and uule token for each. State- and city-level location is not available yet (Location): the response says targeting: false, and a state passed as location or uule returns 400 CITY_LOCATION_UNAVAILABLE. Only country=US is available; any other country returns 400.

JSON
{  "success": true,  "country": "US",  "exitPinning": false,  "targeting": false,  "message": "State- and city-level location is not available yet: a state passed as location returns 400 CITY_LOCATION_UNAVAILABLE. Country (gl) and language (hl) are applied.",  "states": [    { "code": "TX", "name": "Texas", "location": "Texas,United States", "uule": "w+CAIQICIT..." }  ]}

exitPinning: false: results are not localized to the state.

Examples

curl "https://api.searlo.tech/api/v1/credits/balance" \  -H "x-api-key: sk_your_api_key" curl "https://api.searlo.tech/api/v1/credit/balance" \  -H "x-api-key: sk_your_api_key" curl "https://api.searlo.tech/api/v1/search/capabilities" \  -H "x-api-key: sk_your_api_key"
const headers = { "x-api-key": process.env.SEARLO_API_KEY };const res = await fetch("https://api.searlo.tech/api/v1/search/capabilities", { headers });const { capabilities } = await res.json(); // Only call what is on here right nowconst live = new Set(capabilities.filter(c => c.state !== "off").map(c => c.path));console.log(live.has("/serp/html"));
import osimport requests HEADERS = {"x-api-key": os.environ["SEARLO_API_KEY"]}balance = requests.get("https://api.searlo.tech/api/v1/credits/balance", headers=HEADERS).json()print(balance["credits"]["available"], balance["credits"].get("expiresAt"))

Stuck? Write to [email protected], or check live status.