API Endpoints Reference

A reference for all FourA API endpoints with request parameters and response formats.

Base URL

https://eu.api.foura.ai/api

Authentication

Every request requires your API key in the X-API-Key header:

curl -X POST https://eu.api.foura.ai/api/single/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"method": "GET", "url": "https://example.com"}'

Create and manage API keys in the Dashboard. Keys use the pk_live_ prefix.

Response Headers

Every response from /api/* carries two correlation headers:

Header Value Description
X-FourA-Request-Id UUID Unique ID assigned to the request. Returned on every response, including 4xx and 5xx. Log it on your side.
X-FourA-Credits integer Credits spent on this request. Returned on success and on failure (the work was done either way). See Request Outcomes for which outcomes are billable.

The same request ID keys the request and response payload preview in the Dashboard's Activity Log (kept 24 hours, last 200 per key), so you can look up the exact request later and replay it from Activity straight into the Playground. Include it when you contact support, and it pinpoints the request in seconds.

$ curl -i -X POST https://eu.api.foura.ai/api/single/ \
    -H "X-API-Key: YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"method": "GET", "url": "https://example.com"}'

HTTP/2 200
content-type: application/json
x-foura-request-id: 8f3e2a14-7b6c-4d1a-9e2f-5a3b8c1d4e7f
x-foura-credits: 2
...

See Response Headers for the full list and usage tips.

Endpoints

Using these endpoints via MCP? The @fouradata/mcp server wraps all four endpoints as native MCP tools (foura_auto, foura_single, foura_proxy, foura_browser) with the same input shapes plus an offload_large opt-in for token-friendly large-response handling.

FourA provides four request endpoints, each optimized for a different scenario:

Endpoint Best for
POST /auto/ Smart fetch. You pass a URL, FourA picks the cheapest path that works (direct, rotated proxy, or browser) and remembers what works per host.
POST /single/ Fast HTTP requests, static pages, APIs
POST /proxy/ Protected sites with automatic proxy rotation, optional target-visible country scoping
POST /browser/ JavaScript-rendered pages, SPAs
GET /profiles The browser-profile catalogue for single and proxy. Public, no API key.

For a deeper walkthrough of when to pick each, see Choosing the Right Endpoint and the Smart Fetch guide.

Target URL Restrictions

Targets that resolve to private, loopback, or reserved IP ranges (RFC 5735, RFC 6598, IPv6 reserved blocks) are refused with a 400 before the request leaves FourA. Only public hostnames and IPs are forwarded.

{ "error": "Target <ip> resolves to a private/reserved IP" }

Smart Fetch (Auto)

POST /api/auto/

You pass a URL plus optional validate rules. FourA walks a cost-aware ladder (cheap direct probe, rotated proxy, full browser) and stops at the first rung that returns a response your rules accept. On repeat calls to the same host, a warm session is replayed instead, so the second hit is cheap.

You don't tune retries, pool sizes, or proxy counts. FourA learns them per host.

Request Body

Parameter Type Required Default Description
url string Yes - Target URL
method string No "GET" HTTP method
headers [string, string][] No - Custom headers as [name, value] pairs
data any No - Request body for non-GET requests
validate object No - Success criteria, same shape as Single Request's validate (see below). Tell auto what a real page looks like so it can tell content from a challenge page.
returnSession boolean No true Include the winning session (proxy, cookies, userAgent) in the response so you can replay it through /api/single/ or /api/browser/.
forceProxy boolean No true Always route through a rotating proxy. Set false to allow the cheaper direct path when the target permits it (some defenses are stricter on proxy traffic).
timeout_ms integer No 120000 Total time budget for the whole call, in milliseconds. All sub-attempts run inside this budget. Min 5000, max 180000.
ignoreProxies string[] No - Proxy IDs to avoid on every sub-attempt. Use IDs returned by previous /api/auto/ or /api/proxy/ responses.
followRedirects integer No 5 Max redirects to follow on the cheap ladder rungs. 0 to disable. Max 20.

Response

{
  "status": 200,
  "data": "<!doctype html>...",
  "headers": [{"content-type": "text/html"}],
  "meta": {
    "rung": "cache",
    "solved": false,
    "attempts": 1,
    "credits": 2
  },
  "session": {
    "proxy": "A1B2C3",
    "cookies": [{"name": "session", "value": "abc", "domain": "example.com"}],
    "userAgent": "Mozilla/5.0..."
  }
}
Field Type Description
status number HTTP status from the target.
data string or object Response body.
headers array or object Target response headers. Single and proxy rungs return an array of per-hop header objects; browser rungs return a flat object.
meta.rung string Which ladder rung delivered the response. One of: probe (cheap direct request), proxy (rotating proxy), browser (full browser render), cache (warm session replayed), warmup (the site's entry page was fetched first and its cookies opened the deep URL), or fail (no rung produced an accepted response).
meta.solved boolean Whether a bot challenge was solved during this call.
meta.attempts number Sub-attempts made before success.
meta.credits number Total credits spent on this call. Matches X-FourA-Credits.
session.proxy string Encoded ID of the proxy that delivered the response. Reuse it on a Single or Browser request. Present when returnSession is true.
session.cookies array Cookies from the winning attempt. Present when returnSession is true.
session.userAgent string User-Agent used on the winning attempt. Present when returnSession is true.
error string Error message if the call failed.

Example

curl -X POST https://eu.api.foura.ai/api/auto/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/product/42",
    "validate": {"data": {"accept": ["Add to cart"]}}
  }'

Notes

  • Auto is a coordinator. It calls Single, Proxy, or Browser internally and forwards your API key to each sub-call. Each sub-call appears in your Activity Log; the outer /api/auto/ call doesn't add a separate billable row.
  • Pass validate.data.accept with a substring that only the real page contains. Without it, auto can't tell a real 200 from a challenge interstitial returned with status 200.
  • timeout_ms caps the whole call. A cold first hit to a protected site can take tens of seconds; reused warm sessions usually finish in under a second.

Single Request

POST /api/single/

Sends an HTTP request with realistic browser-like wire characteristics, without spinning up a real browser. This is the fastest endpoint.

Request Body

Parameter Type Required Default Description
method string Yes - HTTP method: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
url string Yes - Target URL. Use {ts} anywhere in the URL to insert the current timestamp for cache-busting.
headers [string, string][] No - Custom headers as [name, value] pairs
unblocker boolean No true Send realistic browser headers (User-Agent, Sec-Ch-Ua, Sec-Fetch-*, Accept-Encoding). On by default. Set false to send a plain client signature.
timeout_ms number No 15000 Overall timeout in ms (max: 120000)
connect_timeout_ms number No 5000 Connection timeout in ms
accept_timeout_ms number No 5000 Accept timeout in ms (time to wait for connection acceptance)
server_response_timeout_ms number No 15000 Server response timeout in ms (time to wait for first byte)
dns_cache_timeout_sec number No 120 DNS cache TTL in seconds (max: 240)
followRedirects number No disabled Max redirects to follow (0-20). Omit to disable.
tryJsonData boolean No false Parse response body as JSON if possible
returnBuffer boolean No false Return raw buffer instead of decoded string
data any No - Request body (string or object, auto-serialized to JSON)
proxy string No - Proxy ID from an earlier response, to pin the same exit. Pass the opaque string back verbatim. A raw proxy address is rejected with 400 Invalid proxy format.
browser string No Chrome Browser to present: Chrome, Edge, Safari, Firefox, or Tor. See Browser profiles.
os string No - Operating system to present: Windows, macOS, Android, or iOS. A family name accepts any of its versions.
version string No newest Browser version to present, as listed in the catalogue. The newest match wins when several fit.
profile string No - Exact profile id from GET /api/profiles, instead of the three fields above.
validate object No - Response validation rules (see below)

Browser profiles

By default a request presents the latest Google Chrome. Some targets accept one browser and refuse another, so browser, os, and version narrow a catalogue of measured profiles, and profile selects one by id.

{
  "method": "GET",
  "url": "https://example.com",
  "browser": "Firefox",
  "os": "Windows"
}

Rules:

  • Selection requires unblocker (on by default). With the unblocker off no browser headers are sent, so the request is refused rather than half-applied.
  • When several profiles match, the newest version wins.
  • A combination the catalogue cannot present returns an error naming what is available. The request is never sent as a different browser.
  • The same four fields are available inside the request object of POST /proxy/.

GET /api/profiles returns the full catalogue and needs no API key:

{
  "profiles": [
    { "id": "...", "browser": "Chrome", "version": "...", "os": "...", "osFamily": "macOS" }
  ],
  "default": "..."
}

osFamily is the value to filter on when building a picker; os keeps the release name for display.

Validation Rules

The validate object lets you define conditions for success and failure. If a fail condition matches, the request is treated as failed. If accept conditions are set, only matching responses are treated as successful.

{
  "validate": {
    "status": { "accept": [200, 201], "fail": [403, 503] },
    "headers": { "accept": {"content-type": "application/json"} },
    "data": { "accept": ["product"], "fail": ["captcha", "blocked"] }
  }
}
Field Type Description
validate.status.accept number[] HTTP status codes to accept
validate.status.fail number[] HTTP status codes to reject
validate.headers.accept object Header key-value pairs that must be present
validate.headers.fail object Header key-value pairs that trigger failure
validate.data.accept string[] Strings that must appear in the response body
validate.data.fail string[] Strings in the response body that trigger failure

Example

curl -X POST https://eu.api.foura.ai/api/single/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "method": "GET",
    "url": "https://example.com/products",
    "timeout_ms": 10000
  }'

Response:

{
  "status": 200,
  "headers": [{"result": {"version": "HTTP/2", "code": 200, "reason": ""}, "content-type": "text/html", "server": "nginx", "set-cookie": ["session=abc", "tracker=xyz"]}],
  "data": "<!doctype html>...",
  "total_time": 0.342,
  "proxy": "A1B2C3"
}

When the target runs a bot check on the way to the body, the response also carries a defense object naming the vendor and whether the check was cleared:

{
  "status": 200,
  "data": "<!doctype html>...",
  "total_time": 3.61,
  "defense": {
    "vendor": "sgcaptcha",
    "solved": true,
    "present": ["sgcaptcha"],
    "ms": 3412,
    "cookie": "_I_=<clearance>"
  }
}
Field Type Description
status number HTTP status code from the target
headers array One object per redirect hop. Each has a result field with the status line plus every response header. Multi-value headers (Set-Cookie, Link, WWW-Authenticate) come back as arrays of strings.
data string/object Response body (JSON if tryJsonData is true)
total_time number Total request time in seconds
proxy string Encoded ID of the proxy the request went through (only when a proxy was supplied on the request). Reuse it on a follow-up call to pin the same exit.
defense object Present when the target ran a bot check on this request, or when a retry with the site's own cookies produced the body. defense.solved says whether a check was cleared, defense.retry says whether a retry got you the content. See Anti-Bot Defenses for every field and the full vendor list.
error string Error message if the request failed

Proxy Request

POST /api/proxy/

Routes your request through rotating proxies with automatic retry on failure. Optionally scope selection to a set of target-visible exit countries.

Request Body

Parameter Type Required Default Description
request object Yes - A single request body (same fields as Single Request above)
timeout_ms number No 45000 Overall timeout for all attempts in ms (max: 120000)
maxTries number No 5 Maximum proxy rotation attempts (max: 90)
ignoreProxies string[] No - Proxy IDs to exclude from rotation (use IDs returned by previous responses)
exitCountries string[] No - Strict allowlist of two-letter target-visible country codes (e.g. ["CZ", "GB"]). Values are trimmed, uppercased, and deduplicated. Proxies with unknown exits are excluded and the request never falls back to an unrequested country.

exitCountries scoping

Selection uses the latest available target-visible country metadata, normally refreshed within about ten minutes. It's not a live geolocation lookup during the request. Don't infer the serving country from the proxy host address.

If the current pool has no match for the requested countries, the response returns HTTP 200 with an error envelope:

{
  "error": "No eligible proxy found for exit countries: CZ, GB",
  "code": "no_eligible_proxy",
  "details": { "exitCountries": ["CZ", "GB"] },
  "total": 0.084
}

Preserve the requested scope and retry later. Change or widen it only when your workflow's country requirement explicitly changes.

Example

curl -X POST https://eu.api.foura.ai/api/proxy/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "maxTries": 3,
    "exitCountries": ["CZ", "GB"],
    "request": {
      "method": "GET",
      "url": "https://example.com/prices"
    }
  }'

Response:

{
  "status": 200,
  "headers": [{"result": {"code": 200}, "content-type": "text/html", "set-cookie": ["a=1", "b=2"]}],
  "data": "<!doctype html>...",
  "total_time": 1.204,
  "proxy": "A1B2C3",
  "exitCountry": "CZ",
  "total": 2.341
}
Field Type Description
proxy string Encoded identifier of the proxy used. Reuse it on a Single or Browser request by passing it as the proxy field, or skip it on the next Proxy request via ignoreProxies.
exitCountry string Two-letter target-visible country code of the proxy that served the request. Present only when the request set exitCountries. Always verify it's one of the codes you requested before trusting the response.
total number Outer wall-clock duration in seconds (float). Includes proxy selection, retries, and the successful attempt. total_time is the inner request only; total is always >= total_time.
error string Error message if the request failed. On a scope miss, code is no_eligible_proxy and details.exitCountries echoes the normalized scope.

All Single Request response fields are also included, defense among them: a proxy attempt that met a bot check reports it the same way Single does.


Browser Request

POST /api/browser/

Opens your URL in a Chrome browser instance. The page loads, JavaScript executes, and you get the fully rendered HTML plus the cookie jar.

Request Body

Parameter Type Required Default Description
url string Yes - Target URL
headers object No - Custom headers as key-value pairs
cookies array No - Cookies to set: [{name, value, domain?}]
userAgent string No - Custom User-Agent string
unblocker boolean No true Auto-solve common bot challenges (Cloudflare clearance, similar gates) during page load. On by default. Set false to render whatever the page returns, including a challenge page, without solving.
proxy string No - Proxy ID from an earlier response, to pin the same exit. Pass the opaque string back verbatim. A raw proxy address is rejected with 400 Invalid proxy format.
exitCountry string No - Two-letter country code (ISO 3166-1 alpha-2) of the country the request leaves by. Sets the browser's clock to a timezone that matches. See Matching the browser clock to the exit.
timeout_ms number No 30000 Page load timeout in ms (max: 120000)
checkStatus number No - Expected HTTP status (request fails if different)
checkText string No - Text that must appear in the rendered page

Matching the browser clock to the exit

A page can read the browser's timezone and compare it against the country of the IP it sees. A mismatch is one of the cheapest signals a bot detector has, and it costs you nothing to remove.

Set exitCountry to the country your traffic leaves by and the browser reports a timezone that belongs to it:

{
  "url": "https://example.com",
  "proxy": "A1B2C3",
  "exitCountry": "BR"
}

Rules:

  • The value is the exit country, meaning the country the target sees, not where the proxy is hosted. The two disagree often enough to matter.
  • Omit it and FourA uses the exit country when it knows one, and otherwise leaves the browser clock alone rather than guessing.
  • A country code FourA doesn't recognise is treated the same as leaving the field out. It isn't an error.
  • Only the clock follows the country. Accept-Language and the content the site serves are untouched, so a page won't switch languages on you.

The userAgent parameter

Send userAgent and that exact string is what the page, its workers, and the target all see. FourA also derives the matching client hints from it (sec-ch-ua, sec-ch-ua-platform, navigator.platform, and the high-entropy values a detector asks for by name), so the request doesn't claim one browser in the header and another in JavaScript.

The userAgent in the response is the one that was presented. That matters when you replay a clearance: a cf_clearance cookie is bound to the exit and the User-Agent that earned it, so send back the string the response reported, not the one you think was used. See Anti-Bot Defenses.

Send a non-Chromium string (a Firefox User-Agent, say) and it's presented as-is, with no Chromium brand list attached.

Example

curl -X POST https://eu.api.foura.ai/api/browser/ \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/spa-app",
    "timeout_ms": 15000,
    "checkText": "product-list"
  }'

Response:

{
  "status": 200,
  "headers": {"content-type": "text/html"},
  "body": "<!doctype html>...",
  "cookies": [{"name": "session", "value": "abc123", "domain": "example.com", "path": "/", "expires": 1735689600, "httpOnly": true, "secure": true, "sameSite": "Lax"}],
  "userAgent": "Mozilla/5.0...",
  "defenseSolved": true,
  "defenses": {"present": ["cloudflare"], "cleared": ["cloudflare"]},
  "proxy": "A1B2C3"
}
Field Type Description
status number HTTP status code from the target
headers object Response headers
body string or object Fully rendered page content. String HTML when content-type is HTML; object when the page returned JSON and was auto-parsed.
cookies array Full cookie objects from the page. Each cookie includes name, value, domain, path, expires, httpOnly, secure, sameSite, and other cookie properties.
userAgent string Browser User-Agent used
defenseSolved boolean true if a bot defense was met and genuinely cleared on this call. Absent otherwise. Drives the 15 vs 30 credit cost.
defenses object present lists every vendor recognised during the page load, cleared lists the ones whose clearance the final page holds. A vendor can appear in present and never in cleared. See Anti-Bot Defenses.
proxy string Encoded ID of the proxy the request went through (only when a proxy was supplied on the request). Reuse it on follow-up calls to keep the same exit.
error string Error message if the request failed

HTTP Status Codes

Code Meaning
200 Request completed (check inner status for target response)
400 Invalid request body, parameters, or target IP in a private/reserved range
401 Missing or invalid API key
429 Rate limit exceeded
500 Internal server error
502 Upstream unavailable. FourA reached its engine but the reply was unusable. Retry.
503 Service temporarily disabled or at capacity, or Backend service unavailable while an engine restarts
504 Upstream timeout. The engine didn't finish inside the time budget for this request. Raise timeout_ms or retry.

Next Steps

Last updated: August 19, 2026