Skip to main content
Glama

FourA - HTTP request via rotating proxies

foura_proxy
Read-only

Routes HTTP requests through rotating proxies, retrying on different exits until one delivers. Use it when a single proxy is blocked or the request must originate from a specific exit country.

Instructions

Route an HTTP request through rotating proxies, retrying on another exit until one delivers. Use it when foura_single is blocked, and whenever the exit itself matters: this is the only tool that takes a strict exit-country allowlist (exitCountries, which never falls back to another country), presents a named browser family per attempt, excludes exits you already know are dead (ignoreProxies), and may escalate to a premium exit (exitClass, an allowance rather than an instruction). maxTries bounds how many exits are tried and timeout_ms bounds the whole rotation, so a short timeout can end it before maxTries is reached; protected targets often need 25 to 30. validate decides what counts as delivered, and an attempt it rejects is retried on the next exit. The response returns the id of the exit that succeeded: pass that returned id to foura_single.proxy or foura_browser.proxy to send the next request from the same exit. A failed rotation returns attemptReport, which separates exits that never answered from exits a bot check refused from pages your own rule threw away. Use foura_browser when the page needs JavaScript. Rotation costs several times a single request. One FourA API key authenticates every call, the result reports the credits it spent, and a refusal by your own plan arrives as a plan_limit_ code with retryAfter rather than as a block by the target.

Input Schema

NameRequiredDescriptionDefault
requestYesThe inner HTTP request to send through each proxy attempt. Validation rules here determine when a proxy is treated as failed and retried.
maxTriesNoMaximum rotation attempts before giving up (default 5, max 90). Difficult protected targets may need 25-30. Use exitCountries for country scope.
exitClassNoAllow escalation to a premium exit for a target the standard pool cannot deliver. An allowance, not an instruction: the pool still races and usually wins, and the response reports which class served. `standard` forbids escalation. Without premium exits in the plan the call is refused with code plan_limit_premium.
timeout_msNoOverall timeout across all rotation attempts in ms (default 45000, max 120000). Must be positive.
exitCountriesNoOptional target-visible proxy countries as two-letter provider codes, for example ["CZ", "GB"]. Use codes supplied by the user or target requirements. When geography matters, do not guess codes or substitute unscoped rotation. Values are trimmed, uppercased, and deduplicated. Unknown exits are excluded and the request never falls back to another country.
ignoreProxiesNoEncoded proxy IDs (base36 strings like "4DZ3VE") or proxy URLs to exclude from rotation.
offload_largeNoIf true, response bodies of 50 KB or more are returned as a resource_link instead of inlined. Default false. Read the returned offloaded_resource_uri with resources/read.

Output Schema

NameRequiredDescriptionDefault
codeNoStable error code for retry classification. auth_failed means the FourA API key was rejected; verify that key, not target-site credentials. no_eligible_proxy means the strict exitCountries scope had no match. Keep that scope and retry later; do not propose or perform an unscoped fallback. A plan_limit_* code is the caller's own FourA plan refusing (credits, bandwidth, rate, concurrency, browser_daily, premium, feature), not the target: wait out retryAfter or change the plan, never retry the same work through another tool.
dataNoDecoded response body. Omitted when offloaded.
errorNoHuman-readable error message
proxyNoBase36 ID of the exit that succeeded (e.g. `4DZ3VE`). Pass it to foura_single.proxy or foura_browser.proxy to reuse that exit, or to foura_proxy.ignoreProxies to skip it on future rotations.
totalNoOuter total time in seconds (proxy selection + retries + the successful inner attempt). Float.
limitsNo
statusNoHTTP status code from the target (from the succeeding proxy attempt). `0` indicates every attempt failed before any HTTP response (DNS / connection refused / timeout) - check the `error` field for the underlying reason.
creditsNoCredits this call spent. Reported on failures too: the work was done either way.
currentNo
defenseNoPresent when the target ran a bot check. When solved is false the body may be a challenge page: retry with a different browser, os, or version, or move to foura_browser.
detailsNoStructured no_eligible_proxy context containing the normalized requested country scope. Preserve this scope and retry later. Do not propose or perform an unscoped fallback; change it only after the user explicitly changes the requirement.
headersNoResponse headers per redirect hop, as an array of objects. Each entry has `result.{version, code, reason}` plus arbitrary header-name keys whose values are strings (or arrays of strings for multi-value headers like Set-Cookie / Link).
profileNoThe catalogue profile id the rotation moved to after the target refused the browser this request sent. Absent means it went out as written; when present, pass it as `profile` to present the same browser on the next call.
requestNoEchoed PrRequest from upstream PrResponseError
serviceNo
exitClassNoWhich class delivered, when the request named exitClass. `standard` means the standard pool answered first, which is also the answer once the premium allowance is spent. Neither is an error.
request_idNoFourA's id for this call, for a support request.
retryAfterNo
size_bytesNoTotal offloaded body size in bytes
total_timeNoPer-attempt wall-clock duration of the succeeding inner request
exitCountryNoLatest available two-letter target-visible exit-country code used for selection. Present on successful requests that use exitCountries.
attemptReportNoWhy a failed rotation ran out of tries. Read `summary` first. Counts over `total` attempts: noResponse (the exit never answered), defense (a bot check was recognised, named in `vendors`), contentRejected (HTTP 200, no bot check, rejected only by your validate.data), statusRejected (rejected by your validate.status), other. `profilesTried` lists the browsers sent, `default` meaning the request went out as written. High contentRejected means the pages arrived and your own rule threw them away: fetch once with foura_single and no validate, then rewrite it.
offloaded_resource_uriNofoura-mcp://payload/<uuid>. Pass this URI to resources/read to retrieve the offloaded body.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changedv0.8.2
    • changedOutput schema / properties / profile / description
      Previous value: -"The browser family rotation moved to after the target refused the one this request sent. Absent means it went out as written; when present, replay with it or repeat the version that failed."New value: +"The catalogue profile id the rotation moved to after the target refused the browser this request sent. Absent means it went out as written; when present, pass it as `profile` to present the same browser on the next call."
  2. Changed4 schema fields changedv0.7.3
    • removedInput schema / $schema
      Removed value: -"http://json-schema.org/draft-07/schema#"
    • removedInput schema / properties / request / properties / headers / items / additionalItems
      Removed value: -false
    • changedInput schema / properties / request / properties / headers / items / items
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "string"
      -  }
      -]New value: +{
      +  "type": "string"
      +}
    • removedOutput schema / $schema
      Removed value: -"http://json-schema.org/draft-07/schema#"
  3. Changed12 schema fields changedv0.7.2
    • addedInput schema / properties / exitClass
      Added value: +{
      +  "description": "Allow escalation to a premium exit for a target the standard pool cannot deliver. An allowance, not an instruction: the pool still races and usually wins, and the response reports which class served. `standard` forbids escalation. Without premium exits in the plan the call is refused with code plan_limit_premium.",
      +  "enum": [
      +    "standard",
      +    "premium"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / request / properties / headers / items / additionalItems
      Added value: +false
    • addedInput schema / properties / request / properties / headers / items / maxItems
      Added value: +2
    • addedInput schema / properties / request / properties / headers / items / minItems
      Added value: +2
    • addedOutput schema / properties / attemptReport
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Why a failed rotation ran out of tries. Read `summary` first. Counts over `total` attempts: noResponse (the exit never answered), defense (a bot check was recognised, named in `vendors`), contentRejected (HTTP 200, no bot check, rejected only by your validate.data), statusRejected (rejected by your validate.status), other. `profilesTried` lists the browsers sent, `default` meaning the request went out as written. High contentRejected means the pages arrived and your own rule threw them away: fetch once with foura_single and no validate, then rewrite it.",
      +  "properties": {
      +    "contentRejected": {
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "defense": {
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "noResponse": {
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "other": {
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "profilesTried": {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    },
      +    "statusRejected": {
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "summary": {
      +      "type": "string"
      +    },
      +    "total": {
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "vendors": {
      +      "items": {
      +        "type": "string"
      +      },
      +      "type": "array"
      +    }
      +  },
      +  "type": "object"
      +}
    • changedOutput schema / properties / code / description
      Previous value: -"Stable error code for retry classification. auth_failed means the FourA API key was rejected; verify that key, not target-site credentials. no_eligible_proxy means the strict exitCountries scope had no match. Keep that scope and retry later; do not propose or perform an unscoped fallback."New value: +"Stable error code for retry classification. auth_failed means the FourA API key was rejected; verify that key, not target-site credentials. no_eligible_proxy means the strict exitCountries scope had no match. Keep that scope and retry later; do not propose or perform an unscoped fallback. A plan_limit_* code is the caller's own FourA plan refusing (credits, bandwidth, rate, concurrency, browser_daily, premium, feature), not the target: wait out retryAfter or change the plan, never retry the same work through another tool."
    • addedOutput schema / properties / credits
      Added value: +{
      +  "description": "Credits this call spent. Reported on failures too: the work was done either way.",
      +  "type": "number"
      +}
    • addedOutput schema / properties / exitClass
      Added value: +{
      +  "description": "Which class delivered, when the request named exitClass. `standard` means the standard pool answered first, which is also the answer once the premium allowance is spent. Neither is an error.",
      +  "enum": [
      +    "standard",
      +    "premium"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / profile
      Added value: +{
      +  "description": "The browser family rotation moved to after the target refused the one this request sent. Absent means it went out as written; when present, replay with it or repeat the version that failed.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / request_id
      Added value: +{
      +  "description": "FourA's id for this call, for a support request.",
      +  "type": "string"
      +}
    • removedOutput schema / properties / total_time / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / total_time / type
      Added value: +[
      +  "number",
      +  "string",
      +  "null"
      +]
  4. Changed6 schema fields changedv0.6.0
    • addedInput schema / properties / request / properties / browser
      Added value: +{
      +  "description": "Browser to present: Chrome, Edge, Safari, Firefox, or Tor. Omit every profile field and the request presents the latest Google Chrome.",
      +  "type": "string"
      +}
    • addedInput schema / properties / request / properties / os
      Added value: +{
      +  "description": "Operating system to present: Windows, macOS, Android, or iOS. A family name accepts any of its versions.",
      +  "type": "string"
      +}
    • addedInput schema / properties / request / properties / profile
      Added value: +{
      +  "description": "Exact profile id from the public catalogue at https://api.foura.ai/api/profiles. Use browser/os/version when you do not have an id.",
      +  "type": "string"
      +}
    • changedInput schema / properties / request / properties / unblocker / description
      Previous value: -"Add common browser headers such as User-Agent, Sec-Ch-Ua, and Accept-Encoding. Default false. Enable it for targets that reject basic HTTP requests."New value: +"Send a full browser header set, including User-Agent and Sec-Ch-Ua. Default true. Set false for a plain HTTP request; profile selection needs it on and errors when it is off."
    • addedInput schema / properties / request / properties / version
      Added value: +{
      +  "description": "Browser version to present, as listed in the catalogue. The newest match wins when several fit. An impossible combination returns an error listing what is available; no other browser is substituted.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / defense
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Present when the target ran a bot check. When solved is false the body may be a challenge page: retry with a different browser, os, or version, or move to foura_browser.",
      +  "properties": {
      +    "solved": {
      +      "description": "True when the check was met and `data` is the real page.",
      +      "type": "boolean"
      +    }
      +  },
      +  "type": "object"
      +}
  5. Changed26 schema fields changedv0.5.0
    • addedInput schema / properties / exitCountries
      Added value: +{
      +  "description": "Optional target-visible proxy countries as two-letter provider codes, for example [\"CZ\", \"GB\"]. Use codes supplied by the user or target requirements. When geography matters, do not guess codes or substitute unscoped rotation. Values are trimmed, uppercased, and deduplicated. Unknown exits are excluded and the request never falls back to another country.",
      +  "items": {
      +    "pattern": "^[A-Z]{2}$",
      +    "type": "string"
      +  },
      +  "minItems": 1,
      +  "type": "array"
      +}
    • changedInput schema / properties / ignoreProxies / description
      Previous value: -"Encoded proxy IDs (base36 strings like \"4DZ3VE\") OR proxy URLs to exclude from rotation. Both forms are accepted."New value: +"Encoded proxy IDs (base36 strings like \"4DZ3VE\") or proxy URLs to exclude from rotation."
    • changedInput schema / properties / maxTries / description
      Previous value: -"Maximum proxy rotation attempts before giving up (default 5, max 90). Default 5 is sized for lightly-blocked sites. Raise to 25-30 for tier-1 WAF challenges (Vercel Security Checkpoint, Cloudflare 'Just a moment', Akamai Bot Manager) - most rotations on these targets need this range. If still blocked after 30 attempts, the gate is likely country / ASN allowlist (not solvable by rotation) - pivot strategy instead of climbing to 60."New value: +"Maximum rotation attempts before giving up (default 5, max 90). Difficult protected targets may need 25-30. Use exitCountries for country scope."
    • changedInput schema / properties / offload_large / description
      Previous value: -"If true, response bodies >= 50KB are written to disk and returned as a resource_link instead of inlined. Default false."New value: +"If true, response bodies of 50 KB or more are returned as a resource_link instead of inlined. Default false. Read the returned offloaded_resource_uri with resources/read."
    • addedInput schema / properties / request / properties / accept_timeout_ms / description
      Added value: +"Timeout in ms to receive the first response byte after the request is sent (0-120000). Omit for the default."
    • addedInput schema / properties / request / properties / connect_timeout_ms / description
      Added value: +"Timeout in ms for establishing the connection to the target through the proxy (0-120000). Omit for the default."
    • addedInput schema / properties / request / properties / dns_cache_timeout_sec / description
      Added value: +"How long (seconds) to cache the target's resolved DNS (0-240). Omit for the default."
    • changedInput schema / properties / request / properties / headers / description
      Previous value: -"Custom HTTP headers as [name, value] tuples. Example: "New value: +"Custom HTTP headers as [name, value] tuples. Example: [[\"Accept\", \"application/json\"]]"
    • addedInput schema / properties / request / properties / returnBuffer / description
      Added value: +"Return raw bytes as a serialized Buffer JSON shape ({type:\"Buffer\", data:[byte, ...]}) instead of a decoded string. Use for binary responses (images, protobuf)."
    • addedInput schema / properties / request / properties / server_response_timeout_ms / description
      Added value: +"Timeout in ms for the server to send the complete response (0-120000). Omit for the default."
    • addedInput schema / properties / request / properties / tryJsonData / description
      Added value: +"If true, attempt JSON.parse on the response body; on success `data` is the parsed value, otherwise it stays the original string. Omit to keep the body as-is."
    • changedInput schema / properties / request / properties / unblocker / description
      Previous value: -"Inject realistic browser headers (User-Agent, Sec-Ch-Ua, Accept-Encoding, …) and make the request look like it's coming from a real browser at the wire level. Default false - STRONGLY recommended on proxy paths since most sites that need a proxy also have wire-level anti-bot (Cloudflare, Akamai, PerimeterX, Datadome). Cheap to leave on for production scrapes."New value: +"Add common browser headers such as User-Agent, Sec-Ch-Ua, and Accept-Encoding. Default false. Enable it for targets that reject basic HTTP requests."
    • changedInput schema / properties / request / properties / url / description
      Previous value: -"Target URL the proxy should fetch. Public hosts only - private/reserved ranges (RFC 1918 + loopback + link-local + IPv6 ULA/loopback + *.local mDNS) are refused with code `ssrf_blocked`. Example: https://shop.example.com/pricing for blocked sites. {ts} placeholder is replaced with current Unix timestamp."New value: +"Public target URL. Private or reserved targets return `ssrf_blocked`. Use {ts} in the URL to insert the current Unix timestamp. Example: https://shop.example.com/pricing."
    • addedInput schema / properties / request / properties / validate / description
      Added value: +"Per-attempt response validation. A proxy attempt that fails these checks is treated as failed and the next proxy is tried."
    • addedInput schema / properties / request / properties / validate / properties / data / description
      Added value: +"Body validation: pass when the body contains an expected substring (accept), fail when it contains a blocked one (fail)."
    • addedInput schema / properties / request / properties / validate / properties / data / properties / accept / description
      Added value: +"Substrings the response body must contain to pass."
    • addedInput schema / properties / request / properties / validate / properties / data / properties / fail / description
      Added value: +"Substrings that, if present in the body, mark the response as failed."
    • addedInput schema / properties / request / properties / validate / properties / headers / description
      Added value: +"Header validation: pass when an accepted header matches, fail when a blocklisted header matches."
    • changedInput schema / properties / request / properties / validate / properties / headers / properties / accept / description
      Previous value: -"Map of header-name-substring → header-value-substring (both case-insensitive). Response PASSES if AT LEAST ONE entry matches (header name contains the key AND value contains the value). Checked across all redirect hops. Empty / omitted = no header requirement."New value: +"Case-insensitive header substring rules. The response passes when at least one name/value pair matches across the redirect chain."
    • changedInput schema / properties / request / properties / validate / properties / headers / properties / fail / description
      Previous value: -"Map of header-name-substring → header-value-substring (both case-insensitive). Response is treated as FAILURE if ANY entry matches a response header. Use to reject challenge / block headers, e.g. {\"x-blocked\": \"bot\", \"server\": \"cloudflare\"}."New value: +"Case-insensitive header substring rules that reject the response when any name/value pair matches."
    • addedInput schema / properties / request / properties / validate / properties / status / description
      Added value: +"Status-code validation: which HTTP status codes count as success (accept) or failure (fail)."
    • changedOutput schema / properties / code / description
      Previous value: -"Stable error code for retry classification. One of: ssrf_blocked, upstream_non_json, output_validation_failed, bad_request (400), auth_failed (401), forbidden (403), not_found (404), rate_limited (429), at_capacity (503), service_disabled (503), service_unavailable (503), upstream_error (>=500), upstream_client_error (other 4xx), upstream_unknown (defensive)."New value: +"Stable error code for retry classification. auth_failed means the FourA API key was rejected; verify that key, not target-site credentials. no_eligible_proxy means the strict exitCountries scope had no match. Keep that scope and retry later; do not propose or perform an unscoped fallback."
    • addedOutput schema / properties / details
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Structured no_eligible_proxy context containing the normalized requested country scope. Preserve this scope and retry later. Do not propose or perform an unscoped fallback; change it only after the user explicitly changes the requirement.",
      +  "properties": {
      +    "exitCountries": {
      +      "items": {
      +        "pattern": "^[A-Z]{2}$",
      +        "type": "string"
      +      },
      +      "minItems": 1,
      +      "type": "array"
      +    }
      +  },
      +  "required": [
      +    "exitCountries"
      +  ],
      +  "type": "object"
      +}
    • addedOutput schema / properties / exitCountry
      Added value: +{
      +  "description": "Latest available two-letter target-visible exit-country code used for selection. Present on successful requests that use exitCountries.",
      +  "pattern": "^[A-Z]{2}$",
      +  "type": "string"
      +}
    • changedOutput schema / properties / offloaded_resource_uri / description
      Previous value: -"foura-mcp://payload/<uuid>"New value: +"foura-mcp://payload/<uuid>. Pass this URI to resources/read to retrieve the offloaded body."
    • changedOutput schema / properties / proxy / description
      Previous value: -"Base36 ID of the pool exit that succeeded (e.g. `4DZ3VE`). Reuse on next call: pass to foura_single.proxy or foura_browser.proxy → same exit IP. Pass to foura_proxy.ignoreProxies → skip this exit on future rotations."New value: +"Base36 ID of the exit that succeeded (e.g. `4DZ3VE`). Pass it to foura_single.proxy or foura_browser.proxy to reuse that exit, or to foura_proxy.ignoreProxies to skip it on future rotations."
  6. First observedv0.3.2

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint/openWorldHint/destructiveHint, and the description adds a great deal on top: auth model ('One FourA API key authenticates every call'), credit reporting, plan_limit_ codes with retryAfter, attemptReport breakdown on failure, and the maxTries-vs-timeout interaction ('a short timeout can end it before maxTries is reached'). The one gap is that it never reconciles the read-only annotation with the schema's support for POST/PUT/DELETE/PATCH against remote targets, so the side-effect surface stays ambiguous.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and the sibling comparison, and every sentence carries information with no filler. However, it is a single dense paragraph packing at least seven distinct topics (routing, alternatives, exit control, timeout interplay, validation, exit reuse, cost/auth/errors), which would be easier to scan as structured bullets.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex 7-parameter tool with nested request/validate objects and an output schema, the description covers what the schema cannot: retry semantics, the interaction between maxTries and the overall timeout, how to reuse the returned exit id with foura_single/foura_browser, failure reporting shape, cost, and plan-limit error codes. Nothing an agent needs to invoke it correctly is missing, and it correctly defers return-value detail to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds semantics the schema does not: exitClass is 'an allowance, not an instruction', exitCountries 'never falls back to another country', and a validate rejection triggers a retry on the next exit. It also links maxTries and timeout_ms as competing bounds. It stops short of explaining ignoreProxies/offload_large behavior beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Route an HTTP request through rotating proxies, retrying on another exit'), then explicitly positions itself against siblings: 'Use it when foura_single is blocked' and 'Use foura_browser when the page needs JavaScript.' The claim that it is 'the only tool that takes a strict exit-country allowlist' gives an unambiguous differentiator from foura_auto/foura_single/foura_browser.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit conditions for selection ('whenever the exit itself matters', country allowlisting, dead-exit exclusion, premium escalation) and names the alternatives with the condition that selects them (foura_single when not blocked, foura_browser for JavaScript). It also flags cost proportionality ('Rotation costs several times a single request'), which is real guidance for choosing this over a single call.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Deploy Server

Other Tools