Skip to main content

Confirm

Are you sure?

Async & sync modes

By default, URLpipe processes every request asynchronously — it accepts the request instantly and POSTs the result to your webhook when it's ready. Add sync: true to any request to get the result back inline in the HTTP response instead.

Choosing a mode

Pick the mode per request with the sync parameter. Omit it (or send sync=false) for async — the default, which suits long-running work like /lighthouse and high-volume batches, since you never hold a connection open. Send sync=true for sync — simplest for quick, interactive calls where you want the answer right away.

Typical response times

How long a request takes depends mostly on the target page — its weight, how much JavaScript it runs, and how fast its own server responds. The figures below are drawn from production traffic for non-cached requests: p50 is the median and p90 is the 90th percentile, so 9 in 10 requests finish at or under it.

Endpoint
Type
p50
p90
Web
3.5 s
4.7 s
Web
4.0 s
7.6 s
AI
5.8 s
8.1 s
Web
6.2 s
6.7 s
AI
6.3 s
10.8 s
AI
7.9 s
11.6 s
AI
9.3 s
14.8 s
Web
15.3 s
20.5 s
Cache hits return in well under a second — they do no work, so tuning max_age is the simplest way to make repeat requests fast. AI endpoints add an LLM pass on top of the page fetch, and /lighthouse runs a full audit — the slowest by design, and a natural fit for async.

Async requests

Async is the default, so no sync parameter is needed — just include a report_to parameter, the HTTPS URL URLpipe should deliver the result to. The endpoint responds immediately with a token you can use to correlate the eventual webhook.

Request attributes

  • Name
    url
    Type
    string
    Required
    Required
    Description
    The page to process.
  • Name
    report_to
    Type
    string
    Description
    Webhook URL — an http or https address URLpipe POSTs the result to when it's ready. Required for async requests (the default); omit it only when you send sync=true. A missing or invalid value returns 422.
  • Name
    sync
    Type
    boolean
    Description
    Set to true to process the request synchronously instead. Defaults to false (async). Does not affect the cached result a request maps to.
  • Name
    max_age
    Type
    string | integer
    Description
    How fresh a cached result must be to be accepted. Either an integer number of seconds (3600) or a duration string of the form "<number> <unit>" — units s/min/h/d/w (e.g. "2 hours", "3 days", "30m"). Defaults to 7 days, clamped to a max of 30 days; 0 always bypasses the cache. See Caching for all accepted units.
POST /markdown
curl -X POST https://urlpipe.dev/markdown \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "report_to": "https://your-app.com/webhooks/urlpipe"
  }'
Immediate response
{
  "token": "0Zx3…9aQ",
  "status": "accepted"
}

The webhook delivery

When processing finishes, URLpipe sends a POST with a JSON body to your report_to URL. The token matches the one from the immediate response, so you can pair it with your original request.

Payload fields

  • Name
    token
    Type
    string
    Description
    Correlates with the token returned when you made the request.
  • Name
    operation
    Type
    string
    Description
    The endpoint that ran, e.g. markdown.
  • Name
    success
    Type
    boolean
    Description
    Whether the operation succeeded.
  • Name
    result
    Type
    string | object | null
    Description
    The result on success (a string or JSON value, depending on the endpoint); null on failure.
  • Name
    error
    Type
    string | null
    Description
    A human-readable message on failure; null on success.
On success
{
  "token": "0Zx3…9aQ",
  "operation": "markdown",
  "success": true,
  "result": "# Example Domain\n\nThis domain is for use…",
  "error": null
}
On failure
{
  "token": "0Zx3…9aQ",
  "operation": "markdown",
  "success": false,
  "result": null,
  "error": "The request timed out."
}

Delivery guarantees

  • Results are delivered as a JSON POST to your report_to URL.
  • Webhook delivery uses a 30-second timeout.
  • If delivery fails, the result is still stored and remains visible in your project's history in the dashboard, so nothing is lost.
  • Each request gets a unique token, so concurrent jobs never collide.
  • Prefer to pull instead of receive a webhook? Fetch the result any time with GET /result/:token.
Make your webhook endpoint idempotent and key it on token — treat a repeated token as the same result rather than a new one.

Sync mode

Send sync=true to process a request synchronously. report_to is then ignored: the request is processed inline and the result comes straight back in the HTTP response body, in the content type documented for each endpoint. Failures return a 422 with an error field.

Every sync response also carries an X-Result-Token header identifying the request, so you can re-fetch the result later via GET /result/:token.

Sync timeouts

A sync request waits up to 60 seconds for the analysis to finish. If it isn't ready in time, the endpoint returns 504 Gateway Timeout with the token in the body — but the analysis keeps running in the background. Retrieve the result once it completes via GET /result/:token, or use async mode (omit sync) for consistently long-running work.

504 Gateway Timeout
{
  "error": "processing_timeout",
  "message": "The analysis is taking longer than expected. Retrieve it later via GET /result/:token, or use async mode for long operations.",
  "token": "0Zx3…9aQ"
}