Skip to content
R3XTools

HTTP API

The server-side tools are built on a small public API. It needs no key, is versioned under /api/v1, and every endpoint here is one the interface itself uses. There are no undocumented endpoints you are expected to depend on.

Conventions

Every endpoint takes and returns JSON. Requests carrying an Origin header from a site other than this one are refused — the API is for direct clients and for this interface, not for other people's pages to call from their visitors' browsers.

Failures return a consistent envelope with a machine-readable code and a sentence written for a person. A stack trace is never returned.

{
  "error": {
    "code": "url_not_allowed",
    "title": "URL not allowed",
    "message": "127.0.0.1 is a loopback address. R3X only fetches public internet addresses."
  }
}

Rate-limited responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, plus Retry-After on a 429.

Endpoints

GET/api/v1/statusnone

Report this deployment's limits and storage mode.

Returns the numeric limits every other endpoint enforces, and whether webhook and mock state is held in a shared store or in a single instance's memory.

Response

{
  "version": "v1",
  "storage": { "shared": false, "note": "…" },
  "limits": { "fetch": { … }, "webhooks": { … }, "mocks": { … } }
}
POST/api/v1/http/headers20 per 1 min per IP

Fetch a URL and analyse its response headers.

Sends one GET request and returns every response header plus a structured analysis: security headers with explanations, cookie attributes, CORS, caching and the redirect chain.

FieldTypeNotes
url*stringAn absolute http or https URL on a public address.

Request

curl -X POST https://toolbox.r3x.site/api/v1/http/headers \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}'

Response

{
  "finalUrl": "https://example.com/",
  "status": 200,
  "durationMs": 143,
  "redirects": [],
  "headers": [["content-type", "text/html; charset=utf-8"]],
  "analysis": {
    "security": [
      {
        "name": "Content-Security-Policy",
        "status": "missing",
        "severity": "high",
        "explanation": "Restricts where scripts, styles…",
        "advice": "Start with a report-only policy…"
      }
    ],
    "cookies": [], "cors": [], "caching": [], "disclosure": []
  }
}
POST/api/v1/http/metadata20 per 1 min per IP

Extract a page's title, description and social tags.

Fetches up to 1 MB of an HTML document and returns its metadata plus a list of findings. Non-HTML content types are rejected rather than parsed.

FieldTypeNotes
url*stringAn absolute http or https URL on a public address.

Request

curl -X POST https://toolbox.r3x.site/api/v1/http/metadata \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com"}'

Response

{
  "finalUrl": "https://example.com/",
  "metadata": {
    "title": "Example Domain",
    "description": null,
    "canonical": null,
    "openGraph": [], "twitter": [], "favicons": [],
    "headings": [{ "level": 1, "text": "Example Domain" }]
  },
  "findings": [
    { "level": "warn", "label": "Description missing", "detail": "…" }
  ]
}
POST/api/v1/http/diff10 per 1 min per IP

Fetch two URLs for comparison.

Fetches both URLs in parallel and returns each one's headers, extracted text, formatted HTML, metadata, links and images. The diff itself is computed by the caller.

FieldTypeNotes
urlA*stringFirst URL.
urlB*stringSecond URL.

Request

curl -X POST https://toolbox.r3x.site/api/v1/http/diff \
  -H "Content-Type: application/json" \
  -d '{"urlA":"https://example.com","urlB":"https://example.org"}'

Response

{
  "a": { "finalUrl": "…", "status": 200, "text": "…", "html": "…", "metadata": { … } },
  "b": { "finalUrl": "…", "status": 200, "text": "…", "html": "…", "metadata": { … } }
}
POST/api/v1/http/request20 per 1 min per IP

Forward an arbitrary HTTP request.

Sends the request you describe and returns the response. Nothing about the request or response is logged or stored. Hop-by-hop and connection headers are rejected; an Authorization header is dropped if a redirect crosses origins.

FieldTypeNotes
url*stringTarget URL.
methodstringGET, POST, PUT, PATCH, DELETE, HEAD or OPTIONS. Defaults to GET.
headers[string, string][]Up to 40 header pairs.
bodystring | nullUp to 256 KB, sent as UTF-8.
timeoutMsnumber1000 to 20000. Defaults to 10000.

Request

curl -X POST https://toolbox.r3x.site/api/v1/http/request \
  -H "Content-Type: application/json" \
  -d '{
    "method": "POST",
    "url": "https://api.example.com/users",
    "headers": [["Content-Type", "application/json"]],
    "body": "{\"name\":\"Ada\"}"
  }'

Response

{
  "status": 201,
  "statusText": "Created",
  "durationMs": 212,
  "bodyBytes": 84,
  "headers": [["content-type", "application/json"]],
  "bodyEncoding": "utf8",
  "body": "{\"id\":1,\"name\":\"Ada\"}"
}
POST/api/v1/webhooks10 per 10 min per IP

Create a temporary webhook endpoint.

Returns the endpoint id and a manage token. The token is shown once and only its hash is stored; there is no way to recover it. The endpoint is deleted after 60 minutes.

Request

curl -X POST https://toolbox.r3x.site/api/v1/webhooks

Response

{
  "session": {
    "id": "hk_8F3K92MB",
    "expiresAt": 1757000000000,
    "received": 0,
    "limits": { "maxRequests": 50, "maxBodyBytes": 65536 }
  },
  "token": "…"
}
GET/api/v1/webhooks/{id}240 per 1 min per IP

Read captured requests.

Returns the session and its captured requests, newest first. An unknown id and a wrong token both answer 404, so an id cannot be probed for existence.

Authorization: Bearer <manage token>

Request

curl https://toolbox.r3x.site/api/v1/webhooks/hk_8F3K92MB \
  -H "Authorization: Bearer $TOKEN"

Response

{
  "session": { "id": "hk_8F3K92MB", "received": 2, … },
  "requests": [
    {
      "id": "req_a1b2c3",
      "at": 1756999000000,
      "method": "POST",
      "headers": [["content-type", "application/json"]],
      "bodyKind": "json",
      "body": "{\"event\":\"test\"}",
      "sourceIp": "203.0.113.0"
    }
  ]
}
POST/api/v1/webhooks/{id}/rotate10 per 10 min per IP

Issue a new URL for the same session.

Moves the session to a fresh id, carrying captured requests across, and deletes the old id immediately. The manage token does not change.

Authorization: Bearer <manage token>

DELETE/api/v1/webhooks/{id}240 per 1 min per IP

Delete an endpoint and everything it captured.

Also available as DELETE /api/v1/webhooks/{id}/requests to clear captures but keep the endpoint.

Authorization: Bearer <manage token>

POST/api/v1/mocks10 per 10 min per IP

Create a temporary mock API.

Returns the mock id and a manage token, with one example endpoint defined. Deleted after 24 hours.

Request

curl -X POST https://toolbox.r3x.site/api/v1/mocks

Response

{
  "server": {
    "id": "m_a1b2c3",
    "cors": true,
    "endpoints": [
      { "id": "ep_x1", "method": "GET", "path": "/users", "status": 200, "body": "[…]" }
    ]
  },
  "token": "…"
}
PUT/api/v1/mocks/{id}240 per 1 min per IP

Replace the endpoint definition.

Validates the whole definition and rejects it as a unit. At most 20 endpoints, 32 KB per body, 3000 ms maximum delay. Response headers that would let a definition inject a header line are refused.

Authorization: Bearer <manage token>

FieldTypeNotes
endpoints*MockEndpoint[]The full list, in routing order.
corsbooleanWhether the mock answers CORS preflight and reflects the calling origin.

Request

curl -X PUT https://toolbox.r3x.site/api/v1/mocks/m_a1b2c3 \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "endpoints": [{
      "id": "ep_x1", "method": "GET", "path": "/users",
      "status": 200, "headers": [], "contentType": "application/json",
      "body": "[{\"id\":1,\"name\":\"Alice\"}]", "delayMs": 0, "enabled": true
    }]
  }'
GET/api/v1/mocks/{id}/logs240 per 1 min per IP

Read the mock's request log.

The last 50 requests it answered, newest first, with request and response detail. DELETE the same path to clear it.

Authorization: Bearer <manage token>

Ingest URLs

Two paths exist outside /api/v1 because they are the surfaces other systems call directly rather than an API you consume.

/h/<endpoint-id>
Webhook capture. Accepts every method, answers 200 with a JSON acknowledgement, and records the request. Bodies over 64 KB are truncated.
/m/<mock-id>/<path>
Mock API. Routed against the stored definition; answers 404 with the list of defined routes when nothing matches. Because their bodies are chosen by whoever holds the URL, both paths are served with Content-Security-Policy: sandbox, nosniff, and Content-Disposition: attachment on any type a browser would render as a document. None of it is visible to a programmatic client.

Errors

StatusCodeMeaning
400invalid_requestThe body was missing, malformed, or a required field was absent.
400invalid_urlThe URL could not be parsed.
401unauthorizedA browser request arrived from another origin.
403url_not_allowedThe URL resolves to a private, loopback or reserved address, or uses a scheme or port R3X will not open.
404not_foundUnknown id, expired session, or a manage token that does not match.
409limit_reachedThe session is already holding its maximum number of requests.
410expiredThe endpoint or mock existed but its lifetime has run out.
413payload_too_largeThe request body exceeded the endpoint's ceiling.
429rate_limitedThe rate limit for this bucket is exhausted. Retry-After says when to try again.
502upstream_errorThe target server could not be reached, or its TLS certificate did not verify.
504upstream_timeoutThe target server did not respond within the allowed time.

Fetch restrictions

Every endpoint that fetches a URL on your behalf applies the same rules. Only http and https; only ports 80, 443, 8080 and 8443; no credentials in the URL. The hostname is resolved and every address in the answer must be public — a name resolving to both a public and a private address is refused rather than raced. The socket then connects to that resolved address, so a second DNS answer cannot redirect it.

Redirects are followed up to 5 hops and each hop is validated from scratch. Authorization and Cookie are dropped when a redirect crosses to a different origin. Responses are read up to 2 MB and abandoned mid-stream past that, and a compressed body cannot expand beyond the same ceiling.

Stability

There is no authentication and no API key in this phase. Anonymous limits are conservative and the design leaves room to attach higher limits to a key later, without changing the shape of any endpoint here. If you build on this, pin to /api/v1; a breaking change would land under a new version rather than in place. See the toolbox for the interfaces built on it.