--- title: Production Reliability | Tabstack description: Caching behavior, retry handling, error patterns, timeouts, and operational decisions for running Tabstack dependably in production. --- This guide covers what you need to know to run Tabstack dependably in production: caching behavior, when to bypass it, retry handling, error patterns, and the operational decisions that matter at scale. --- ## Caching Tabstack caches extraction results by URL. When you request the same URL again, the API may return a cached result rather than re-fetching the page. This is usually what you want: it’s faster and reduces cost. **What’s cached:** The response for a given URL + endpoint combination. Effort level and schema are also factors; changing them may produce a fresh fetch. **How long results are cached:** Cache TTL is not publicly documented. For time-sensitive data (pricing, inventory, live stats), always set `nocache: true`. **When to use `nocache: true`:** - Pricing or availability data that changes frequently - Pages you know have updated since your last fetch - Debugging extraction issues (confirms the problem isn’t a stale cache) - Any data with a business requirement for freshness ``` // Price monitoring — always fresh const result = await client.extract.json({ url: 'https://competitor.com/pricing', nocache: true, json_schema: { /* ... */ } }) ``` --- ## Automatic retries The SDK automatically retries failed requests **twice** with exponential backoff. The following errors are retried: | Status | Error | | ------ | ----------------------------- | | 408 | Request Timeout | | 409 | Conflict | | 429 | Rate Limit | | 500+ | Server errors | | N/A | Network / connection failures | Retries happen transparently. You don’t need to implement retry logic for these cases. **Disable retries if you’re running your own retry orchestration:** ``` const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY, maxRetries: 0 }) // Or per-request: await client.extract.json({ url, json_schema }, { maxRetries: 5 }) ``` --- ## Error handling patterns ### Handle 422 explicitly A `422 UnprocessableEntityError` means the page was fetched but extraction failed. This is the most actionable error; you can often fix it: ``` import Tabstack, { UnprocessableEntityError } from '@tabstack/sdk' try { return await client.extract.json({ url, json_schema }) } catch (err) { if (err instanceof UnprocessableEntityError) { // Try with full rendering return await client.extract.json({ url, json_schema, effort: 'max', nocache: true }) } throw err } ``` ### Rate limit handling 429 errors are auto-retried, but if you’re hitting them consistently you’re exceeding your plan’s rate limit. Use exponential backoff in your own queuing layer for bulk operations: ``` import { RateLimitError } from '@tabstack/sdk' async function extractWithBackoff( url: string, schema: object, attempt = 0 ): Promise { try { return await client.extract.json({ url, json_schema: schema }) } catch (err) { if (err instanceof RateLimitError && attempt < 3) { const delay = Math.pow(2, attempt) * 1000 await new Promise(r => setTimeout(r, delay)) return extractWithBackoff(url, schema, attempt + 1) } throw err } } ``` ### Auth errors in production 401 `AuthenticationError` in production usually means a key was rotated or the environment variable wasn’t set in the deployment. These are not retried automatically; they require intervention. ``` import { AuthenticationError } from '@tabstack/sdk' if (err instanceof AuthenticationError) { // Alert — this is a configuration problem, not a transient failure alertOps('Tabstack API key invalid or missing') } ``` --- ## Timeouts Default timeout is **60 seconds** per request. `effort: 'max'` on complex SPAs can approach this. Tune per endpoint: ``` // Longer timeout for heavy pages const result = await client.extract.json( { url, json_schema, effort: 'max' }, { timeout: 90_000 } // 90 seconds ) ``` For `/automate` and `/research` (streaming), the timeout governs the initial connection, not the stream duration. Long-running tasks may continue streaming well past 60 seconds. --- ## Logging Enable debug logging during development to see full request/response detail: ``` const client = new Tabstack({ apiKey: process.env.TABSTACK_API_KEY, logLevel: 'debug' // 'debug' | 'info' | 'warn' | 'error' | 'off' }) ``` In production, `warn` (default) is appropriate: it surfaces retry events and errors without noise. For Python, set `TABSTACK_LOG=info` in the environment. --- ## Bulk extraction patterns For high-volume extraction jobs (many URLs), control concurrency to avoid rate limit errors: ``` import PLimit from 'p-limit' const limit = PLimit(5) // 5 concurrent requests const results = await Promise.all( urls.map(url => limit(() => client.extract.json({ url, json_schema })) ) ) ```