Integration Guide — Content Retrieval & Search — v1
The People Inc Content API lets licensed clients retrieve the editorial content of any People Inc article as clean, structured markdown. The markdown is suitable for direct ingestion into LLM prompts, RAG pipelines, or any system that benefits from plain-text document representation.
The API exposes two complementary endpoints:
GET /v1/markdown) — fetch a known
article by canonical URL.
POST /v1/search) — ask a natural-language
question and receive the best matching licensed articles, ranked and ready for RAG or LLM
grounding.
Both endpoints share the same authentication, rate-limit, and error conventions. This guide covers inputs, outputs, and integration patterns for each.
https://contentmarketplace.people.inc.
v1, reflected in the /v1/ path prefix. Non-breaking
additions (e.g. new optional fields) may land in v1 without notice. A breaking
change would be introduced under a new path (e.g. /v2/) rather than changing
v1's existing behavior in place.
Get a key, make one request, read the response. Full details on each step are linked below.
X-API-Key header (see Authentication):
GET /v1/markdown
(see Content retrieval)
POST /v1/search
(see Search)
# Retrieve an Investopedia article curl -G https://contentmarketplace.people.inc/v1/markdown \ --data-urlencode "url=https://www.investopedia.com/how-experts-recommend-managing-a-usd50k-inheritance-for-lasting-financial-securi-12004329" \ -H "X-API-Key: YOUR_API_KEY"
{
"status": 200,
"markdown": "## How Experts Recommend Managing a $50K Inheritance\n\n...",
"metadata": { /* ... */ }
}
# Ground a question with ranked licensed sources curl -X POST https://contentmarketplace.people.inc/v1/search \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d '{ "question": "best meal prep tips for beginners" }'
{
"question": "best meal prep tips for beginners",
"documents": [ /* ranked grounding results */ ],
"selection_summary": { /* ... */ }
}
That's it — you have a working integration. See Getting started checklist for the full pre-launch checklist, including error handling for statuses you'll see in normal operation.
Every request to either endpoint must include an X-API-Key header containing the
API key issued to your organization by the People Inc Content Marketplace team.
| Header | Value | |
|---|---|---|
X-API-Key |
Your issued API key | Required |
A request with a missing or invalid key returns 401 Unauthorized.
See Status codes for the full error shape.
Each API key is rate limited independently per route. By default, a key may make up to
100 requests per second against each of /v1/markdown and
/v1/search. Requests beyond that limit receive a 429 Too Many Requests
response.
Every 429 response includes a Retry-After header:
| Header | Description |
|---|---|
Retry-After |
Number of seconds to wait before retrying. |
// 429 — rate limit exceeded HTTP/1.1 429 Too Many Requests Retry-After: 1 { "type": "about:blank", "status": 429, "title": "Too Many Requests", "detail": "Rate limit exceeded. Retry after the period indicated by Retry-After.", "instance": "/v1/markdown?url=..." }
Every successful (200) response from /v1/markdown is recorded as a
billable event. 304 Not Modified responses to conditional requests (see
Markdown response format) and error responses
(4xx/5xx) are not billed.
Billing for /v1/search is governed by your licensing agreement. Contact the
Content Marketplace team with questions about search usage or billing.
GET /v1/markdownReturns the markdown representation of the People Inc article at the given canonical URL.
| Parameter | Type | Description | |
|---|---|---|---|
url |
string (URI) |
The canonical URL of the People Inc article you want to retrieve.
Must be a fully-qualified URL. Use the https:// form and the canonical form of the URL — no trailing slash or query string.
|
Required |
| Header | Type | Description | |
|---|---|---|---|
X-API-Key |
string | Your issued API key. See Authentication. | Required |
If-None-Match |
string |
Conditional-request header for caching. Pass the ETag value from a previous
200 response for the same url. If the article content is unchanged,
the API responds 304 Not Modified with an empty body; if it has changed, you
receive a normal 200 with the new content and a new ETag.
See Markdown response format for details.
|
Optional |
# Retrieve an Investopedia article curl -G https://contentmarketplace.people.inc/v1/markdown \ --data-urlencode "url=https://www.investopedia.com/how-experts-recommend-managing-a-usd50k-inheritance-for-lasting-financial-securi-12004329" \ -H "X-API-Key: YOUR_API_KEY"
POST /v1/search
Accepts a natural-language question and returns ranked, licensed People Inc articles suitable
for grounding an AI response. Results include the same clean markdown available
from GET /v1/markdown, so a single search call can both discover and retrieve
source text.
documents is a source you should ground with: ranked, licensed, and
returned with the full article markdown. selection_summary describes the returned set as a whole.
Send a JSON object with Content-Type: application/json.
| Field | Type | Default | Description | |
|---|---|---|---|---|
question |
string (max 2000 chars) | — |
The natural-language question to ground (e.g. "best meal prep tips for beginners").
Must contain non-whitespace content — empty or whitespace-only values are rejected.
Leading/trailing whitespace is trimmed.
|
Required |
limit |
integer (1–5) | 5 |
Maximum number of results returned. Ranking considers a larger candidate pool
internally; limit only caps how many of the best picks come back. For
time-sensitive questions, only sufficiently relevant, recent content is returned, so fewer
than limit results (possibly none) can come back.
|
Optional |
strategy |
"balanced" |
"balanced" |
Ranking strategy preset. Only balanced is available in v1.
|
Optional |
| Header | Type | Description | |
|---|---|---|---|
X-API-Key |
string | Your issued API key. See Authentication. | Required |
Content-Type |
string | Must be application/json. |
Required |
# Search with defaults curl -X POST https://contentmarketplace.people.inc/v1/search \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d '{ "question": "best meal prep tips for beginners" }' # Search with limit curl -X POST https://contentmarketplace.people.inc/v1/search \ -H "Content-Type: application/json" \ -H "X-API-Key: YOUR_API_KEY" \ -d '{ "question": "best meal prep tips for beginners", "limit": 2 }'
The response is a JSON object with these top-level fields:
| Field | Type | Description |
|---|---|---|
question |
string | The question as submitted. |
documents |
array |
Ranked grounding results, up to limit. Every entry is a result you should
use.
|
selection_summary |
object | Aggregate stats over the selection. See below. |
documents[]Every entry is a grounding result — use them all.
| Field | Type | Description |
|---|---|---|
doc_id |
string | Stable document identifier. |
url |
string | Canonical article URL. Suitable for citation and for a follow-up GET /v1/markdown call. |
domain |
string | Publisher / brand domain label for the article. |
rank |
integer |
1-based position in documents. Always >= 1 when
results are present — never 0.
|
content |
object |
Object with a markdown string (full article body) and the same
metadata shape returned by GET /v1/markdown (see
section 10).
|
selection_summary| Field | Type | Description |
|---|---|---|
selected_count |
integer | How many documents were returned as grounding picks (length of documents). |
domain_count |
integer | Number of distinct publisher domains in the selected set. |
total_wordcount |
integer | null |
Sum of content.metadata.wordcount across the selected set.
|
{
"question": "best meal prep tips for beginners",
"documents": [
{
"doc_id": "12345678",
"domain": "Lifestyle",
"url": "https://www.allrecipes.com/article/meal-prep-guide/",
"rank": 1,
"content": {
"markdown": "# The Ultimate Guide to Meal Prep for Beginners\n\n...",
"metadata": {
"title": "The Ultimate Guide to Meal Prep for Beginners",
"wordcount": 1850,
"sourcetype": 0
}
}
}
],
"selection_summary": {
"selected_count": 5,
"domain_count": 4,
"total_wordcount": 1850
}
}
documents in order (already sorted by rank) and pass
each content.markdown (or url + a follow-up retrieval) into your LLM
context.
| Code | Meaning | Applies to | What to do |
|---|---|---|---|
| 200 | Success | Both |
Markdown: read the markdown field.
Search: use every entry in documents.
|
| 304 | Not modified | /v1/markdown |
Returned for a conditional request (If-None-Match) when the content is
unchanged since the supplied ETag. The body is empty — reuse your cached copy.
See Markdown response format.
|
| 401 | Unauthorized | Both | Your X-API-Key is missing or invalid. Verify the key and header name. |
| 403 | Not licensed | /v1/markdown |
The article exists but is not available under your licensing agreement. Contact the Content Marketplace team if you believe this is in error. |
| 404 | Not found | /v1/markdown |
If the article has moved, the Problem Details body includes a redirectUrl
field — re-request that URL (see the "Moved articles" guidance in
section 12). Otherwise, no content is available at the given URL. This can mean the URL is
simply wrong, or that the article existed but is no longer available — for example it was
unpublished, archived, or removed. The response does not distinguish between these cases;
verify the article still exists on People Inc and that the URL is spelled correctly.
|
| 422 | Invalid request | Both |
Markdown: the url parameter is missing or is not a valid URI.
Search: the JSON body failed validation (e.g. missing, empty, or whitespace-only
question, question over 2000 characters, or limit
out of range). Check the errors array in the Problem Details body.
|
| 429 | Too many requests | Both |
You have exceeded your API key's rate limit for that route. Check the
Retry-After header and wait that many seconds before retrying.
See Rate limits.
|
| 500 | Server error | Both | An unexpected error occurred on our side. Retry with exponential backoff. If the issue persists, contact the People Inc Content Marketplace team. |
The response body is a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
status |
number | The HTTP status code (200). |
markdown |
string | The full article content rendered as markdown. |
metadata |
object | Article metadata. See the field table below. |
metadata fields:
| Field | Type | Description |
|---|---|---|
title |
string | Optional. The article title. |
wordcount |
number | Optional. Word count of the article body. |
pubdate |
string (ISO 8601) | Optional. When the article was first published. |
update |
string (ISO 8601) | Optional. When the article was last updated. |
author |
array of strings | Optional. Author name(s). |
language |
string | The article's language/locale (e.g. en-US). |
sourcetype |
number | Content origin per the CoMP convention: 0 = human created, 1 = AI created, 2 = hybrid. |
cattax |
number | Optional. IAB category taxonomy in use. Always 9 (IAB Content Category Taxonomy 3.1) for our content today. |
cat |
array of strings |
Optional. IAB Tech Lab content category values for the article, per
the cattax taxonomy. Omitted when no category could be matched.
|
published |
number | Optional. CoMP publication status: 1 = publicly available. Always 1 for our content today. |
provenance |
number | Optional. Whether content-provenance data (e.g. C2PA) is available: 0 = no, 1 = yes. Always 0 for our content today. |
ext |
object |
Optional. Implementer-specific extensions. Carries only
bylineBlock (array of strings — formatted names for every non-author byline
type, e.g. fact-checkers and reviewers) for our content today.
|
{
"status": 200,
"markdown": "## How Experts Recommend Managing a $50K Inheritance\n\n...",
"metadata": {
"title": "How Experts Recommend Managing a $50K Inheritance",
"wordcount": 1240,
"pubdate": "2023-11-14T22:13:20.000Z",
"update": "2023-11-16T02:40:00.000Z",
"author": ["Jane Doe"],
"language": "en-US",
"sourcetype": 0,
"cattax": 9,
"cat": ["216"],
"published": 1,
"provenance": 0,
"ext": {
"bylineBlock": ["John Smith, MD"]
}
}
}
Every 200 response also includes the following header:
| Header | Description |
|---|---|
ETag |
A quoted opaque identifier for this version of the article's markdown
(e.g. "d41d8cd98f00b204e9800998ecf8427e"). The same content always
yields the same ETag, and it changes whenever the underlying article changes.
Store it and send it back via If-None-Match on your next request to revalidate.
|
When you send an If-None-Match header whose value matches the current
ETag, the API responds 304 Not Modified with an
empty body. Your previously cached markdown is still current —
reuse it. If the content has changed, you instead receive a normal 200 with the
updated markdown and a new ETag. This lets polling clients revalidate cheaply
without re-downloading unchanged articles.
All error responses follow RFC 9457 Problem Details:
| Field | Type | Description |
|---|---|---|
type | string (URI) | A URI identifying the error type. |
status | number | The HTTP status code. |
title | string | Short human-readable summary of the error type. |
detail | string | Human-readable explanation of this specific occurrence. |
instance | string | The request path that produced this error. |
errors | array | 422 only. Array of validation error objects, each with a path, message, and code. |
redirectUrl | string | 404 only, when the article has moved. A new /v1/markdown request URL (relative) to re-request instead. |
requestId | string (UUID) | Optional. Include this in any support request — it correlates this error to server-side logs. |
// 401 — missing key { "type": "about:blank", "status": 401, "title": "Unauthorized", "detail": "X-API-Key header is required", "instance": "/v1/markdown?url=..." } // 422 — invalid url parameter { "type": "about:blank", "status": 422, "title": "Unprocessable Entity", "detail": "Invalid request payload", "instance": "/v1/markdown?url=not-a-url", "errors": [{ "path": ["url"], "message": "Invalid URL", "code": "invalid_format" }] } // 404 — article not found { "type": "about:blank", "status": 404, "title": "Request Error", "detail": "Document not found", "instance": "/v1/markdown?url=...", "requestId": "f75d48b1-9c57-4844-a51b-8027c4fd7260" }
The markdown returned by GET /v1/markdown and by
documents[].content.markdown on POST /v1/search follows a
consistent set of conventions designed for LLM ingestion. Understanding these will help you
parse or post-process the content reliably.
# (h1) heading at the top of the document.## (h2).### (h3) label with the callout title — for example ### Tip or ### Key Takeaways.[text](url).### Request GET /v1/markdown?url=https%3A%2F%2Fwww.investopedia.com%2Fhow-experts-recommend-managing-a-usd50k-inheritance-for-lasting-financial-securi-12004329 X-API-Key: YOUR_API_KEY ### Response — 200 OK { "status": 200, "markdown": "## How Experts Recommend Managing a $50K Inheritance for Lasting Financial Security\n\nInheriting money can feel overwhelming...\n\n### Key Takeaways\n\n> - Consult a fee-only financial advisor before making any moves.\n> - Pay off high-interest debt first.\n\n## What To Do First...", "metadata": { "title": "How Experts Recommend Managing a $50K Inheritance for Lasting Financial Security", "wordcount": 1240, "pubdate": "2023-11-14T22:13:20.000Z", "update": "2023-11-16T02:40:00.000Z", "author": ["Jane Doe"], "language": "en-US", "sourcetype": 0, "cattax": 9, "cat": ["216"], "published": 1, "provenance": 0 } }
The first response returns an ETag. On the next poll, send it back via
If-None-Match; if nothing changed you get a 304 with no body and can
reuse your cached copy.
### First request GET /v1/markdown?url=https%3A%2F%2Fwww.investopedia.com%2Fhow-experts-recommend-managing-a-usd50k-inheritance-for-lasting-financial-securi-12004329 X-API-Key: YOUR_API_KEY ### Response — 200 OK ETag: "d41d8cd98f00b204e9800998ecf8427e" { "status": 200, "markdown": "## How Experts Recommend..." } ### Follow-up request — revalidate with the ETag GET /v1/markdown?url=https%3A%2F%2Fwww.investopedia.com%2Fhow-experts-recommend-managing-a-usd50k-inheritance-for-lasting-financial-securi-12004329 X-API-Key: YOUR_API_KEY If-None-Match: "d41d8cd98f00b204e9800998ecf8427e" ### Response — 304 Not Modified (empty body; reuse cached markdown)
### Request POST /v1/search Content-Type: application/json X-API-Key: YOUR_API_KEY { "question": "best meal prep tips for beginners", "limit": 5 } ### Response — 200 OK { "question": "best meal prep tips for beginners", "documents": [ { "doc_id": "12345678", "url": "https://www.allrecipes.com/article/meal-prep-guide/", "domain": "Lifestyle", "rank": 1, "content": { "markdown": "# The Ultimate Guide to Meal Prep for Beginners\n\n...", "metadata": { "title": "The Ultimate Guide to Meal Prep for Beginners", "sourcetype": 0 } } } ], "selection_summary": { "selected_count": 5, "domain_count": 4 } }
### Request GET /v1/markdown?url=https%3A%2F%2Fwww.investopedia.com%2Fold-article-slug-12345 X-API-Key: YOUR_API_KEY ### Response — 404 Not Found { "type": "about:blank", "status": 404, "title": "Not Found", "detail": "Document has moved", "instance": "/v1/markdown?url=...", "redirectUrl": "/v1/markdown?url=https%3A%2F%2Fwww.investopedia.com%2Fnew-article-slug-12345" }
### Response — 404 Not Found { "type": "about:blank", "status": 404, "title": "Request Error", "detail": "Document not found", "instance": "/v1/markdown?url=...", "requestId": "f75d48b1-9c57-4844-a51b-8027c4fd7260" }
// Minimal typed wrapper — adapt baseUrl and key storage to your environment const BASE_URL = "https://contentmarketplace.people.inc"; interface ArticleMetadata { title?: string; wordcount?: number; pubdate?: string; update?: string; author?: string[]; language: string; sourcetype: number; cattax?: number; cat?: string[]; published?: number; provenance?: number; ext?: { bylineBlock?: string[] }; } interface ArticleResponse { status: number; markdown: string; metadata: ArticleMetadata; } // Thrown instead of a plain Error when the article has moved, so callers can // branch on it explicitly rather than have the redirect silently followed. // A redirect can mean the requested article was replaced, corrected, or taken // down (e.g. under embargo) — the content at redirectUrl is not guaranteed to // be "the same article". If the caller is acting on a human's behalf (for // example, an LLM agent), confirm with the human before re-requesting rather // than assuming the new URL satisfies their original request. class ArticleMovedError extends Error { constructor(public readonly redirectUrl: string) { super(`Article moved: ${redirectUrl}`); } } async function getArticle(articleUrl: string, apiKey: string): Promise<ArticleResponse> { const endpoint = new URL("/v1/markdown", BASE_URL); endpoint.searchParams.set("url", articleUrl); const response = await fetch(endpoint.toString(), { headers: { "X-API-Key": apiKey }, }); if (!response.ok) { const err = await response.json(); if (err.redirectUrl) { throw new ArticleMovedError(new URL(err.redirectUrl, BASE_URL).toString()); } throw new Error(`[${response.status}] ${err.detail ?? err.title}`); } return response.json(); } // Caller decides whether to re-request — do not do this automatically. try { const article = await getArticle("https://www.example.com/old-article-slug", apiKey); } catch (err) { if (err instanceof ArticleMovedError) { // Confirm with the user before fetching err.redirectUrl. } }
5xx responses are safe to retry. Use exponential backoff with jitter, starting at
1 second and capping at 30 seconds. Do not retry 4xx responses — they
indicate a problem with the request itself.
When an article's canonical URL has changed, the API responds 404 with a
redirectUrl field in the Problem Details body (see
section 9) rather than issuing an HTTP redirect. Check for
this field on every 404 response.
redirectUrl does not guarantee the destination is the same article — redirects
also occur when content is replaced, corrected, or taken down (e.g. under embargo). Treat it as
a candidate, not a guaranteed match. If your integration acts on behalf of a human (for example,
an LLM agent retrieving content per a user's request), confirm with the user before re-requesting
the new URL rather than silently substituting different content for what they asked for.
The url query parameter must be percent-encoded. Use your language's standard
URI encoding function (encodeURIComponent in JavaScript,
urllib.parse.quote in Python, etc.) rather than encoding it manually.
A common mistake is passing the URL unencoded, which causes the article URL's path to be
misinterpreted as part of the request path.
GET /v1/markdown request using the curl example in
section 6 with a known article URL. Verify a 200
with a non-empty markdown field.
POST /v1/search request using the curl example in
section 7. Verify a 200 with at least one
documents entry.
X-API-Key header into your HTTP client — ensure the key is read from an environment variable or secrets manager, not hardcoded.404s on markdown (check for a redirectUrl before treating them as
not-found), 403 not-licensed, 422 validation errors, and
429 rate-limiting, which will occur in normal operation.
For any questions or support, please contact the People Inc Content Marketplace team at [email protected].