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

# REST API Integration

> Access your articles programmatically via REST API

The ChatFeatured REST API allows you to query, create, and manage your articles programmatically. Use API keys to authenticate requests and build custom workflows, integrations, or applications on top of your ChatFeatured content.

**Status:** Available | **Complexity:** Intermediate | **Use cases:** Custom automation, third-party integrations, mobile apps

<Info>
  The REST API is perfect for building custom integrations, automating publishing workflows, or syncing content to external platforms. It gives you full programmatic control over your articles.
</Info>

***

## Authentication

All API requests require authentication via API Key:

### Headers

```text theme={null}
Authorization: Bearer YOUR_API_KEY
```

or

```text theme={null}
X-API-Key: YOUR_API_KEY
```

### Create an API Key

1. In ChatFeatured, go to **Integrations → REST API**
2. Click **Create API Key**
3. Name it descriptively (e.g., "Mobile App", "Automation Script")
4. Copy the key immediately (only shown once)

<Warning>
  **Security:** Treat API Keys like passwords. Never commit them to version control or expose in client-side code. Store them in environment variables instead.
</Warning>

***

## Base URL

All API requests use:

```text theme={null}
https://api.chatfeatured.com/v1
```

<Tip>
  Use HTTPS only. HTTP requests will be rejected for security.
</Tip>

***

## Endpoints

### List Articles

List all articles with optional filtering.

```text theme={null}
GET /articles
```

**Query Parameters:**

| Parameter | Type   | Description                                         |
| --------- | ------ | --------------------------------------------------- |
| `status`  | string | Filter: `published`, `draft`, `scheduled`, `review` |
| `limit`   | number | Max results per page (default: 20, max: 100)        |
| `offset`  | number | Pagination offset (default: 0)                      |

**Example with curl:**

```bash theme={null}
curl https://api.chatfeatured.com/v1/articles?status=published&limit=10 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Example with Python:**

```python theme={null}
import requests

response = requests.get(
    'https://api.chatfeatured.com/v1/articles',
    params={'status': 'published', 'limit': 10},
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
articles = response.json()
```

**Example with JavaScript:**

```javascript theme={null}
const response = await fetch(
  'https://api.chatfeatured.com/v1/articles?status=published&limit=10',
  {
    headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
  }
);
const articles = await response.json();
```

**Response:**

```json theme={null}
{
  "data": [
    {
      "id": "abc123",
      "title": "AI Trends in 2026",
      "slug": "ai-trends-2026",
      "status": "published",
      "excerpt": "What's new in AI this year...",
      "content": "<p>Full HTML content...</p>",
      "featuredImage": "https://...",
      "createdAt": "2026-05-20T10:00:00Z",
      "publishedAt": "2026-05-21T14:30:00Z"
    }
  ],
  "pagination": {
    "offset": 0,
    "limit": 10,
    "total": 42
  }
}
```

***

### Get Single Article

Retrieve a specific article by ID or slug.

```text theme={null}
GET /articles/{id}
GET /articles/{slug}
```

**Example with curl:**

```bash theme={null}
curl https://api.chatfeatured.com/v1/articles/ai-trends-2026 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Example with Python:**

```python theme={null}
response = requests.get(
    'https://api.chatfeatured.com/v1/articles/ai-trends-2026',
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
article = response.json()
```

**Example with JavaScript:**

```javascript theme={null}
const response = await fetch(
  'https://api.chatfeatured.com/v1/articles/ai-trends-2026',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const article = await response.json();
```

***

### Publish Article

Publish a draft article or change publication status.

```text theme={null}
POST /articles/{id}/publish
```

**Body:**

```json theme={null}
{
  "status": "published",
  "publishedAt": "2026-05-22T14:00:00Z"
}
```

**Example with curl:**

```bash theme={null}
curl -X POST https://api.chatfeatured.com/v1/articles/abc123/publish \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "published"}'
```

**Example with Python:**

```python theme={null}
response = requests.post(
    'https://api.chatfeatured.com/v1/articles/abc123/publish',
    json={'status': 'published'},
    headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
```

**Example with JavaScript:**

```javascript theme={null}
const response = await fetch(
  'https://api.chatfeatured.com/v1/articles/abc123/publish',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_API_KEY'
    },
    body: JSON.stringify({ status: 'published' })
  }
);
```

***

## Webhooks

Get notified when articles change status via webhooks.

<Info>
  Webhooks let your application react to events in real-time. For example, automatically sync articles to your CMS when they're published, or send Slack notifications when articles are ready for review.
</Info>

### Configure Webhooks

1. Go to **Integrations → REST API**
2. Scroll to **Webhooks** section
3. Enter your **Webhook URL**
4. (Optional) Enter a **Signing Secret** for security
5. Select which events to receive:
   * Article ready for review
   * Article published
6. Save webhook settings

### Webhook Events

**Event: `article.review`**

Fires when an article finishes generating and is ready for review.

```json theme={null}
{
  "event": "article.review",
  "timestamp": "2026-03-20T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "title": "Your Article Title",
    "slug": "your-article-title",
    "status": "review",
    "excerpt": "A brief excerpt...",
    "content": "<p>Full HTML content...</p>",
    "metadata": {
      "articleType": "guide",
      "description": "A brief excerpt..."
    }
  }
}
```

**Event: `article.published`**

Fires when an article is published.

```json theme={null}
{
  "event": "article.published",
  "timestamp": "2026-03-20T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "title": "Your Article Title",
    "slug": "your-article-title",
    "status": "published",
    "publishedAt": "2026-03-20T12:00:00.000Z"
  }
}
```

### Webhook Security

If you configured a **Signing Secret**, each webhook includes an `X-Webhook-Signature` header:

```text theme={null}
X-Webhook-Signature: sha256=hex_encoded_signature
```

**Verify the signature with Python:**

```python theme={null}
import hmac
import hashlib

def verify_webhook(payload, signature, secret):
    expected = hmac.new(
        secret.encode(),
        payload.encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
```

**Verify the signature with JavaScript:**

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(expected, signature);
}
```

<Tip>
  Always verify webhook signatures to ensure requests are genuinely from ChatFeatured.
</Tip>

***

## Error Handling

<Accordion title="401 Unauthorized">
  ```json theme={null}
  {
    "error": "unauthorized",
    "message": "Invalid or missing API key"
  }
  ```

  **Cause:** Invalid or missing API Key\
  **Solution:** Generate new API Key, verify header format (`Authorization: Bearer KEY` or `X-API-Key: KEY`)
</Accordion>

<Accordion title="404 Not Found">
  ```json theme={null}
  {
    "error": "not_found",
    "message": "Article not found"
  }
  ```

  **Cause:** Article doesn't exist or wrong ID/slug\
  **Solution:** Verify article ID or slug, check if article was deleted
</Accordion>

<Accordion title="429 Rate Limited">
  ```json theme={null}
  {
    "error": "rate_limited",
    "message": "Too many requests. Retry after 60 seconds"
  }
  ```

  **Cause:** Too many requests in short time\
  **Solution:** Wait time in `X-RateLimit-Reset` header, implement exponential backoff, reduce frequency
</Accordion>

<Accordion title="500 Server Error">
  ```json theme={null}
  {
    "error": "server_error",
    "message": "An internal error occurred"
  }
  ```

  **Cause:** Unexpected server error\
  **Solution:** Retry with exponential backoff, check status page, contact support if persists
</Accordion>

***

## Rate Limiting

API requests are rate limited per API Key:

* **Free tier:** 100 requests/hour
* **Pro tier:** 1,000 requests/hour
* **Enterprise:** Custom limits

Response headers indicate your remaining quota:

```text theme={null}
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1653313200
```

<Warning>
  Check rate limit headers in every response. When you're close to the limit, throttle requests or implement backoff.
</Warning>

***

## Use Cases

### Automated Publishing

```python theme={null}
import requests
import schedule

def publish_article():
    response = requests.post(
        'https://api.chatfeatured.com/v1/articles/article-123/publish',
        json={'status': 'published'},
        headers={'Authorization': f'Bearer {API_KEY}'}
    )
    print(f"Published: {response.json()}")

schedule.every().day.at("09:00").do(publish_article)
```

### Sync to External CMS

```python theme={null}
def sync_to_wordpress(article_id):
    # Get article from ChatFeatured
    cf_response = requests.get(
        f'https://api.chatfeatured.com/v1/articles/{article_id}',
        headers={'Authorization': f'Bearer {CF_API_KEY}'}
    )
    article = cf_response.json()
    
    # Post to WordPress
    wp_response = requests.post(
        'https://yourwordpress.com/wp-json/wp/v2/posts',
        json={
            'title': article['title'],
            'content': article['content'],
            'status': 'publish'
        },
        auth=('user', 'password')
    )
    return wp_response.status_code
```

### Monitor Article Status

```python theme={null}
def check_article_status():
    response = requests.get(
        'https://api.chatfeatured.com/v1/articles?status=review&limit=100',
        headers={'Authorization': f'Bearer {API_KEY}'}
    )
    
    articles = response.json()['data']
    print(f"Articles waiting for review: {len(articles)}")
    for article in articles:
        print(f"- {article['title']}")
```

***

## Best Practices

* Store API Keys in environment variables
* Use different keys for different applications
* Implement exponential backoff for retries
* Validate webhook signatures
* Handle errors gracefully

<Warning>
  **Don't** commit API Keys to version control\
  **Don't** share API Keys across applications\
  **Don't** expose Keys in client-side code\
  **Don't** ignore rate limit headers
</Warning>

***

## Troubleshooting

<Accordion title="'401 Unauthorized' Error">
  **Cause:** Invalid API Key

  **Solution:**

  * Generate new API Key in ChatFeatured
  * Verify key is not expired
  * Check header format: `Authorization: Bearer KEY` or `X-API-Key: KEY`
  * Verify no extra spaces or truncation in key
</Accordion>

<Accordion title="'429 Rate Limited' Error">
  **Cause:** Too many requests

  **Solution:**

  * Check `X-RateLimit-Reset` header for wait time
  * Implement exponential backoff (1s, 2s, 4s, etc.)
  * Reduce request frequency
  * Upgrade plan if you need higher limits
</Accordion>

<Accordion title="Webhooks Not Firing">
  **Cause:** URL is unreachable or returns error

  **Solution:**

  * Verify webhook URL is publicly accessible
  * Ensure endpoint returns **200 OK** response
  * Check server logs for incoming requests
  * Test with a tool like [webhook.cool](https://webhook.cool)
  * Verify webhook secret is correct if configured
</Accordion>

***

## See Also

* [CMS Integrations](/integrations/wordpress) — Publish to other platforms
* [Index Submissions](/index-submissions/index-submissions) — Submit URLs programmatically
