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

# Best Practices

> Harden your integration with proper error handling, timeouts, rate limits, and testing strategies.

## Handle Timeouts Gracefully

Most requests answer well under a second, but the worst case is about 6 seconds: our own steps and the wait for demand partners are each capped. Always set a client-side timeout a little above that, and show no ad if it fires:

```javascript theme={null}
try {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 7000);
  
  const response = await fetch(url, {
    signal: controller.signal,
    // ... rest of config
  });
  
  clearTimeout(timeout);
  return await response.json();
} catch (error) {
  console.error('Bid request failed:', error);
  return null;
}
```

## Respect Rate Limits

The API allows **100 requests per second per publisher**. Your staging and production keys share that limit. Monitor rate limit headers:

```javascript theme={null}
const rateLimitRemaining = response.headers.get('X-RateLimit-Remaining');
const rateLimitReset = response.headers.get('X-RateLimit-Reset');

if (rateLimitRemaining < 10) {
  console.warn('Approaching rate limit');
}
```

## Maintain Conversation Context

Send everything you have about the conversation: `messages`, a `context.summary`, `context.keywords`, or any mix. Keywords alone work if you can't share the conversation text. Keep summaries current and keywords relevant. If you send more than the last message, keep the history accurate: same order, nothing dropped from the middle, and timestamps from when each message was actually sent. See [`context`](https://docs.thrad.ai/api/api-reference/bid-request-v2#context).

```javascript theme={null}
let conversationHistory = [];

function addMessage(role, content) {
  conversationHistory.push({
    role: role,
    content: content,
    timestamp: Math.floor(Date.now() / 1000)
  });
}

function resetConversation() {
  conversationHistory = [];
  sessionStorage.removeItem('thrad_context_id');
}
```

## Error Handling

Treat anything other than a bid as "no ad", and keep your UI working. Every status and `code` is listed in [Errors](https://docs.thrad.ai/api/api-reference/bid-request-v2.md#errors):

* **`200` with empty `bids`** — no ad this time; `no_bid_reason` says why. Show nothing.
* **`4xx`** — branch on `error.code`, not `message`. `400 invalid_request` lists what was wrong in `error.fields`: fix it, don't retry it.
* **`429`** — back off and retry later.
* **`5xx` or a timeout** — skip the ad for this turn.

## Maximising Fill Rate

Fill rate is the share of bid requests that return an ad. It comes from match quality, not from asking the server to serve regardless of match:

1. **Send `device.geo.country`, `device.type` and `device.os` on every request** — the biggest lever. Without the country, every campaign that targets one is skipped; without `type` and `os`, device-targeted campaigns can't match. `ip` and `user_agent` are only fallbacks.
2. **Send the conversation** — more of the exchange, with PII stripped, gives the auction more to match against.
3. **Don't cap `title.max_chars` tighter than you need** — every limit filters out some ads.

## Support

For questions or assistance, reach out to [contact@thrad.ai](mailto:contact@thrad.ai)

## Next steps

> [card] [Go live](https://docs.thrad.ai/go-live.md)
>
> Production key and ads.txt: the two steps before real ads serve.

> [card] [Revenue & Metrics](https://docs.thrad.ai/insights/overview.md)
>
> Read your revenue and delivery numbers over the API.

Need exact fields? The [API Reference](https://docs.thrad.ai/api/api-reference/bid-request-v2.md) has every request and response.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.