> ## Documentation Index
> Fetch the complete documentation index at: https://docs.context.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Timeouts and partial results

> Set an API deadline, choose how to handle unfinished work, and preserve partial-result markers.

Set `timeoutOpts` on an endpoint that supports request deadlines. `milliseconds` sets the budget and `behavior` controls what happens when work is unfinished:

| Behavior         | At the deadline                                                                                           |
| ---------------- | --------------------------------------------------------------------------------------------------------- |
| `fail` (default) | Return an unbilled `408 REQUEST_TIMEOUT` if the request has not completed.                                |
| `return-partial` | Return usable completed work with a completion marker. If no usable result exists, fail without a charge. |

## Request a partial page

This Markdown request allows 30 seconds and accepts the page content available at the deadline. Use `fail` when your workflow needs a completed capture.

<Note>
  These examples use the SDKs' low-level request methods to send the current API contract, including fields not yet exposed by the 2.14.0 typed helpers. Authentication, retries, and error handling still come from the SDK. See [SDK compatibility](/optimization/api-stability#sdks-can-lag-the-server-contract).
</Note>

<CodeGroup>
  ```bash cURL theme={null}
  curl --get https://api.context.dev/v1/web/scrape/markdown \
    --header "Authorization: Bearer $CONTEXT_DEV_API_KEY" \
    --data-urlencode "url=https://example.com" \
    --data-urlencode "timeoutOpts[milliseconds]=30000" \
    --data-urlencode "timeoutOpts[behavior]=return-partial"
  ```

  ```typescript TypeScript theme={null}
  import ContextDev from "context.dev";

  const client = new ContextDev({ apiKey: process.env.CONTEXT_DEV_API_KEY });

  const response = await client.get<Record<string, unknown>>("/web/scrape/markdown", {
    query: {
      "url": "https://example.com",
      "timeoutOpts[milliseconds]": 30000,
      "timeoutOpts[behavior]": "return-partial"
    }
  });

  console.log(response.markdown, response.finalDOMState);
  ```

  ```python Python theme={null}
  import os
  from context.dev import ContextDev

  client = ContextDev(api_key=os.environ["CONTEXT_DEV_API_KEY"])

  response = client.get(
      "/web/scrape/markdown",
      cast_to=dict[str, object],
      options={"params": {'url': 'https://example.com',
       'timeoutOpts[milliseconds]': 30000,
       'timeoutOpts[behavior]': 'return-partial'}},
  )

  print(response["markdown"], response.get("finalDOMState"))
  ```

  ```ruby Ruby theme={null}
  require "cgi/core"
  require "context_dev"

  client = ContextDev::Client.new(api_key: ENV.fetch("CONTEXT_DEV_API_KEY"))

  response = client.request(
    method: :get,
    path: "web/scrape/markdown",
    query: {
      "url": "https://example.com",
      "timeoutOpts[milliseconds]": 30000,
      "timeoutOpts[behavior]": "return-partial"
    }
  )

  puts response[:markdown], response[:finalDOMState]
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"os"

  	contextdev "github.com/context-dot-dev/context-go-sdk/v2"
  	"github.com/context-dot-dev/context-go-sdk/v2/option"
  )

  func main() {
  	client := contextdev.NewClient(option.WithAPIKey(os.Getenv("CONTEXT_DEV_API_KEY")))
  	var response map[string]any
  	err := client.Get(context.Background(), "/web/scrape/markdown", nil, &response,
  		option.WithQuery("url", "https://example.com"),
  		option.WithQuery("timeoutOpts[milliseconds]", "30000"),
  		option.WithQuery("timeoutOpts[behavior]", "return-partial"),
  	)
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println(response["markdown"], response["finalDOMState"])
  }
  ```

  ```php PHP theme={null}
  <?php
  require __DIR__.'/vendor/autoload.php';

  use ContextDev\Client;

  $client = new Client(apiKey: getenv('CONTEXT_DEV_API_KEY'));

  $raw = $client->request(
      method: 'get',
      path: 'web/scrape/markdown',
      query: [
          "url" => "https://example.com",
          "timeoutOpts[milliseconds]" => 30000,
          "timeoutOpts[behavior]" => "return-partial",
      ],
  );

  $response = json_decode((string) $raw->getBody(), true, flags: JSON_THROW_ON_ERROR);
  echo $response['markdown'], PHP_EOL;
  print_r($response['finalDOMState'] ?? null);
  ```
</CodeGroup>

For POST requests, put the object in the JSON body:

```json theme={null}
{
  "timeoutOpts": {
    "milliseconds": 30000,
    "behavior": "return-partial"
  }
}
```

GET requests accept the deep-object keys shown above or a JSON-encoded `timeoutOpts` query value. The API deadline is separate from your HTTP client's transport timeout. Allow time for the server to return its result, and account for SDK retries when setting your application's total latency budget.

## Choose a valid budget

| Operations                                                                  |                      Minimum `milliseconds` | Partial results                                 |
| --------------------------------------------------------------------------- | ------------------------------------------: | ----------------------------------------------- |
| HTML, Markdown, Images, Screenshot, Styleguide, Fonts                       | 1 with `fail`; 15,000 with `return-partial` | Captured content with `finalDOMState`.          |
| Sitemap                                                                     |                                           1 | Collected URLs with `partial: true`.            |
| Brand, Crawl, Search, Extract, Answers, People Enrich, products, NAICS, SIC |                                       1,000 | Completed, validated data with `partial: true`. |
| Bytes                                                                       |                                           1 | Fail-only; the download must complete.          |
| Prefetch                                                                    |                                       1,000 | Fail-only; it acknowledges queued work.         |

The maximum is 300,000 milliseconds. Endpoint-specific limits still apply: for example, Answers has a 30-second Fast or 50-second Ultra research budget that a caller timeout can shorten but cannot extend. Parse does not accept `timeoutOpts`.

If you supply `waitForMs`, the deadline must allow the wait plus 10,000 milliseconds for page loading and capture. A zero wait is normalized to 500 milliseconds. An incompatible pair returns `400 TIMEOUT_TOO_SHORT_FOR_WAIT`; increase the budget or lower the wait. Brand retrieval can also reject a budget below 10 seconds for an uncached domain with `422 COLD_DOMAIN_TIMEOUT_TOO_LOW`.

## Preserve completion markers

Browser captures return `finalDOMState`:

* `loaded`: the page completed the waits requested by the call.
* `still-loading`: partial mode captured the DOM when the deadline arrived; content that renders later may be missing.

Interrupted structured responses use `partial: true`. Keep that marker with stored data and surface it when completeness matters. A response can be valid JSON and still contain only part of the requested dataset. Search can also expose `finalDOMState` on a result's captured Markdown.

Partial extraction must satisfy the caller's schema. Partial Brand profiles omit unknown fields, and partial product results can omit unfinished image processing. Partial Answers require usable evidence and a valid response shape. A successful HTTP status does not imply complete research or a complete catalog.

## Billing and caching

Usable partial results are successful, billable responses under the operation's pricing. Partial HTML, Markdown, and Images use the base request price without unfinished browser-action, OCR, or enrichment charges. Answers charges the selected mode's price for a valid partial answer.

A deadline failure without usable results is not billed. Incomplete results are not cached as complete results. Use [cache freshness controls](/optimization/best-practices#set-cache-freshness) separately when deciding whether an existing complete result is recent enough.

## Migrate existing requests

The legacy `timeoutMS` parameter remains accepted and behaves like `timeoutOpts: { milliseconds: timeoutMS, behavior: "fail" }`. An explicit `timeoutOpts` takes precedence. New request schemas expose `timeoutOpts`; SDK 2.14.0 typed helpers can still expose the legacy field, so use the supported low-level methods above when sending the new object.

### Product catalog deadlines

[Product catalog extraction](/api-reference/web-extraction/products) previously returned partial success when an explicit `timeoutMS` interrupted collection. An explicit deadline now returns an unbilled `408` with legacy syntax, omitted behavior, or `behavior: "fail"`.

To accept an incomplete catalog, send:

```json theme={null}
{
  "timeoutOpts": {
    "milliseconds": 30000,
    "behavior": "return-partial"
  }
}
```

Preserve `partial: true` and request more time when you need a fuller catalog. Requests without an explicit timeout keep their existing best-effort behavior.

See [Troubleshooting](/optimization/troubleshooting#handle-timeouts) for failure recovery and [API stability](/optimization/api-stability) for compatibility guidance.
