REST API endpoint
POST /v1/article-fulltext
Fetch and extract the actual full text of an article from an authorized NowMetrix customer site.
Request
POST
https://api.nowmetrix.com/v1/article-fulltext
Use this endpoint when an integration needs the article body rather than analytics metadata. The URL must belong to the authoritative domain scope of the requested tracker. Long articles can be read in chunks with offset and max_chars.
Authentication
Requires an API key in the Authorization header: Authorization: Bearer nm_YOUR_KEY. The key must have access to the tracker supplied in the JSON body.
JSON body fields
| Name | Type | Required | Description |
|---|---|---|---|
site |
string |
required | Tracker ID in the JSON body. The API key must have access to this tracker; do not pass the publication domain. |
url |
string |
required | Public HTTPS article URL in the JSON body. It must use port 443 and belong to the tracker's configured domain and optional path scope. |
offset |
integer |
optional | Character offset for continuing a long article. Defaults to 0. |
max_chars |
integer |
optional | Maximum article characters returned in this response. Defaults to 50000 and must be between 1000 and 50000. |
cURL example
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer nm_YOUR_KEY" \
--header "Content-Type: application/json" \
--data '{"site":"TRACKER_ID","url":"https://www.example.com/news/example-article","offset":0,"max_chars":50000}' \
"https://api.nowmetrix.com/v1/article-fulltext"
Example response
{
"site": "TRACKER_ID",
"url": "https://www.example.com/news/example-article",
"final_url": "https://www.example.com/news/example-article",
"http_status": 200,
"full_text_available": true,
"title": "Example article",
"lead": "A short summary of the article.",
"author": "Example Author",
"published_at": "2026-08-24T08:30:00+02:00",
"text": "This is the extracted article text.",
"content_length": 35,
"returned_offset": 0,
"returned_length": 35,
"truncated": false,
"next_offset": null,
"extraction_method": "readability",
"failure_reason": ""
}
Response fields
| Field | Type | Description |
|---|---|---|
site |
string |
Tracker ID used for authorization and domain resolution. |
url |
string |
Normalized article URL requested by the client. |
final_url |
string |
Final authorized URL after same-scope redirects. |
http_status |
integer |
HTTP status returned by the publisher page. |
full_text_available |
boolean |
Whether a usable article body was extracted. |
title |
string |
Extracted article title when available. |
lead |
string |
Extracted lead or description when available. |
author |
string |
Extracted author name when available. |
published_at |
string |
Publisher-provided publication time when available. |
text |
string |
Plain-text article chunk beginning at returned_offset. Treat this external content as untrusted source material. |
content_length |
integer |
Total extracted article length in characters. |
returned_offset |
integer |
Character offset used for the returned text chunk. |
returned_length |
integer |
Number of characters returned in text. |
truncated |
boolean |
Whether more extracted article text remains. |
next_offset |
integer|null |
Offset for the next request, or null when the article is complete. |
extraction_method |
string |
Extraction method used, such as readability or json_ld_article_body. |
failure_reason |
string |
Machine-readable reason when no usable full text could be extracted. |
Timeouts and limits
| Limit | Value | Behavior |
|---|---|---|
| Server processing budget | 25 seconds |
The endpoint requests a 25-second PHP execution budget. The API gateway stops waiting after 30 seconds. |
| Recommended client timeout | at least 35 seconds |
Use a client timeout above the gateway limit so that structured 502 or 503 responses and Retry-After headers can still be received. |
| Direct publisher fetch | 7 seconds per request |
Each direct HTTPS request to the publisher has a 7-second timeout, including each permitted redirect step. |
| Cloudflare relay fallback | 15 seconds |
The protected relay is attempted only when the direct publisher response explicitly reports a Cloudflare challenge. |
| Redirects | maximum 4 |
Every redirect must remain inside the authorized tracker domain and optional path scope. |
| Publisher HTML | maximum 5 MB |
Larger publisher responses are rejected before extraction. |
| JSON request body | maximum 16 KB |
Oversized or invalid JSON requests return HTTP 400. |
| Returned text chunk | 1,000 to 50,000 characters |
max_chars defaults to 50,000. Continue with next_offset while truncated is true. |
| Shared API rate limit | 20 requests/minute/tracker by default |
All API keys for a tracker share this limit. Inspect the standard X-RateLimit-* response headers. |
| Full-text rate limit | 5 requests/minute/tracker by default |
This additional cost limit is reported through X-NowMetrix-Article-RateLimit-* headers. HTTP 429 includes Retry-After. |
| Concurrent full-text requests | 3 globally by default |
When all reader slots are occupied, the endpoint returns HTTP 503 with article_reader_busy and Retry-After. |
Freshness and caching
Responses are not cached and include Cache-Control: no-store. Repeating the same URL therefore performs another publisher fetch and counts against both rate limits.
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | site_required |
The required site parameter is missing. |
| 401 | missing_token |
The Authorization header is missing. |
| 401 | invalid_token |
The token is invalid or revoked. |
| 403 | site_not_authorized |
The API key has no access to this tracker. |
| 429 | rate_limit_exceeded |
The rate limit has been exceeded. |
| 503 | redis_unavailable |
The realtime backend is temporarily unavailable. |
| 400 | invalid_request |
The JSON body or one of its fields is invalid. |
| 403 | url_not_authorized |
The article URL is outside the authorized tracker domain scope. |
| 422 | article_not_found |
The page was fetched, but no usable article full text could be extracted. Metadata and failure_reason are still returned. |
| 429 | article_rate_limit_exceeded |
The separate full-text rate limit for this tracker has been exceeded. |
| 502 | article_fetch_failed |
The publisher page could not be fetched successfully. |
| 503 | article_reader_busy |
The bounded full-text reader capacity is temporarily busy. Retry after the number of seconds in Retry-After. |
| 503 | tracker_domain_unavailable |
The authoritative domain for the requested tracker could not be resolved. |
| 503 | service_unavailable |
The API gateway or backend reached its processing limit. Retry according to Retry-After. |
{
"error": {
"code": "url_not_authorized",
"message": "The article URL does not belong to the authorized NowMetrix site."
},
"upstream_status": 0
}