Handling Search Responses and Errors

Learn how to parse Bonai News API responses, handle pagination, errors, and rate limits in your application.

Understanding the Response Structure

Every successful response from the Bonai News API follows a consistent structure. Understanding it helps you parse results reliably.

{
  "metadata": {
    "id": "b1037b3b-ce89-4c7e-b540-b2ecb44c3c0f",
    "took_ms": 448,
    "timestamp": "2026-10-13T15:19:53.542Z"
  },
  "parameters": {
    "query": "stock market",
    "language": "en",
    "paywall": false,
    "page": 1,
    "limit": 25,
    "sort": "relevance"
  },
  "articles": [
    {
      "id": "a53fa77ebd1ca2de7702abab8a75b0d3",
      "title": "Article headline",
      "url": "https://example.com/article",
      "excerpt": "A brief summary of the article content.",
      "thumbnail": "https://example.com/image.jpg",
      "language": "en",
      "paywall": false,
      "content_length": 4520,
      "date": "2025-07-21T14:30:00+00:00",
      "authors": ["Author Name"],
      "keywords": ["Topic", "Category"],
      "translations": null,
      "publisher": {
        "name": "Publisher Name",
        "url": "https://example.com",
        "favicon": "https://example.com/favicon.ico"
      }
    }
  ],
  "pagination": {
    "total_hits": 1523,
    "page": 1,
    "hits_per_page": 25,
    "total_pages": 61,
    "has_more": true
  }
}

Pagination

The API returns results in pages. Use pagination.has_more to keep paging until there are no more results, or pagination.total_pages to cap the loop.

async function searchAllPages(query, maxPages = 5) {
  const allArticles = [];

  for (let page = 1; page <= maxPages; page++) {
    const response = await fetch(
      `https://api.bonai.io/news/search/articles?query=${encodeURIComponent(query)}&page=${page}`,
      {
        headers: {
          "api-key": "YOUR_API_KEY",
          "api-version": "2026-08-01",
        },
      }
    );

    const json = await response.json();

    if (!response.ok) {
      throw new Error(
        json.errors?.[0]?.message || `Request failed (${response.status})`
      );
    }

    allArticles.push(...json.articles);

    if (!json.pagination?.has_more) {
      break;
    }
  }

  return allArticles;
}

Error Handling

Check the HTTP status code before processing data. Failures return metadata plus an errors array, where each entry carries a code, a message, and details.

async function searchArticles(query) {
  const response = await fetch(
    `https://api.bonai.io/news/search/articles?query=${encodeURIComponent(query)}`,
    {
      headers: {
        "api-key": "YOUR_API_KEY",
        "api-version": "2026-08-01",
      },
    }
  );

  const json = await response.json();

  if (!response.ok) {
    const first = json.errors?.[0];
    if (response.status === 429) {
      throw new Error("Rate limit exceeded. Wait before retrying.");
    }
    throw new Error(
      first?.message || `Request failed with status ${response.status}`
    );
  }

  return json.articles;
}

Rate Limits

Each plan includes a monthly request limit. When you reach 85% of your limit, the API sends a notification. After exceeding your limit, extra requests are billed at a flat overage rate.

Best practices:

  • Cache responses when possible to reduce API calls
  • Use the metadata.took_ms field to monitor response times
  • Implement exponential backoff on 429 errors
  • Track your usage in the Bonai console

Working with Translations

When the source article is not in English, translations.en holds the English title and excerpt.* It is null when the article is already in English or no translation is available.

const article = json.articles[0];

// Article fields are in the source language
const originalTitle = article.title;

// English translation, when available
const englishTitle = article.translations?.en?.title ?? originalTitle;
const englishExcerpt = article.translations?.en?.excerpt ?? article.excerpt;

Next Steps

*Translations cover the title and excerpt (snippet) only — the full article body is not translated.