Partner API
Partner API integration guide
The Freespoke Partner API lets publishers submit content for indexing and monitor indexing status. This guide is for external developers building a direct integration.
If you would like to integrate the Freespoke Partner API, contact us at [email protected].
Base URL
All endpoints are served over HTTPS:
https://api.partners.freespoke.comAuthentication
All requests require a bearer token sent via the Authorization header:
Authorization: Bearer <token>There are two ways to obtain a token:
API key
If you have been issued an API key, use it directly as the bearer token. No additional exchange is required.
curl -H "Authorization: Bearer $PARTNERS_API_TOKEN" \ https://api.partners.freespoke.com/v1/content/epochOAuth2 client credentials grant
If you have been issued a client ID and client secret, exchange them for a short-lived access token using the OAuth 2.0 Client Credentials Grant. The token endpoint is a standard OpenID Connect token endpoint, so any RFC 6749-compliant OAuth2 library can handle the token exchange and refresh automatically.
Token endpoint:
https://accounts.freespoke.com/realms/freespoke/protocol/openid-connect/tokenToken request:
curl -X POST https://accounts.freespoke.com/realms/freespoke/protocol/openid-connect/token \ -d grant_type=client_credentials \ -d client_id=$PARTNER_CLIENT_ID \ -d client_secret=$PARTNER_CLIENT_SECRETToken response:
{ "access_token": "eyJ...", "token_type": "Bearer", "expires_in": 300}Use the access_token value as the bearer token for subsequent API requests. Tokens expire after a short period (indicated by expires_in in seconds) and should be refreshed when they expire.
Security notes
Treat API keys and client secrets like passwords. Do not embed them in client-side apps or commit them to version control.
Data model
Article (index request)
Required fields:
url(string, absolute URL)title(string)content(string)publish_time(RFC3339 timestamp)authors(array with at least one author)
Optional fields:
description(string)keywords(array of strings, max 32)image_url(string, absolute URL)content_medium(string enum; useMEDIUM_WEBPAGE)test_mode(boolean; defaults tofalse)
Notes:
- Your
urlmust belong to a domain approved for your organization. - HTML content is accepted; the API will normalize it for indexing.
- Use UTC timestamps (for example
2024-01-01T00:00:00Z). - When
test_modeistrue, the request is validated and processed end-to-end, but the resulting content is not persisted and no ingest event is emitted. Use it to verify your integration without publishing real content.
Author (person)
Required fields:
name(string)
Optional fields:
id(string)url(string)bias(float)twitter_id(string)facebook_id(string)
Indexing flow
Indexing is asynchronous. You submit an article, receive a job ID, then poll job status.
1) Submit content
POST /v1/content
Example:
curl -X POST https://api.partners.freespoke.com/v1/content \ -H "Authorization: Bearer $PARTNERS_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com/article", "title": "Example Title", "description": "Short summary", "content": "<p>Hello world</p>", "authors": [ { "id": "author-1", "name": "Jane Doe" } ], "keywords": ["news", "example"], "publish_time": "2024-01-01T00:00:00Z", "image_url": "https://example.com/image.jpg", "content_medium": "MEDIUM_WEBPAGE" }'Response:
{ "jobId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "workflowId": "partner-ingest/123456"}If the request fails validation or is rejected, the response may include errorMessage.
2) Poll job status
GET /v1/job/{job_id}
Example:
curl -H "Authorization: Bearer $PARTNERS_API_TOKEN" \ https://api.partners.freespoke.com/v1/job/9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6dResponse:
{ "job": { "jobId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "status": "JOB_STATUS_COMPLETE", "createTime": "2024-01-01T00:00:00Z", "updateTime": "2024-01-01T00:00:10Z", "error": { "code": 0, "message": "", "details": [] }, "metadata": { "@type": "type.googleapis.com/...", "value": "..." }, "result": { "@type": "type.googleapis.com/...", "value": "..." } }}Status values:
JOB_STATUS_PENDINGJOB_STATUS_COMPLETEJOB_STATUS_FAILED
When a job reaches JOB_STATUS_COMPLETE, its result is a google.protobuf.StringValue whose value is the document’s IndexItem id. Record this id — you can pass it to DELETE /v1/content to target removal precisely.
Removing content
If a piece of content is taken down on your site, request its removal from the Freespoke index.
DELETE /v1/content
Pass the URL as a query parameter. Optionally include the id previously returned by an indexing job — when present it is sanity-checked against the URL.
curl -X DELETE -H "Authorization: Bearer $PARTNERS_API_TOKEN" \ "https://api.partners.freespoke.com/v1/content?url=https%3A%2F%2Fexample.com%2Farticle"Response:
{ "id": "9b1deb4d3b7d4bad9bdd2b0d7b3dcb6d12345678" }The id echoed back is the document’s IndexItem id. The request must come from an organization that owns the URL’s host; otherwise 412 Failed Precondition is returned. Removal is best-effort and propagates asynchronously.
Epoch (re-indexing)
The API exposes an epoch to help you detect when re-indexing is required.
GET /v1/content/epoch
curl -H "Authorization: Bearer $PARTNERS_API_TOKEN" \ https://api.partners.freespoke.com/v1/content/epochResponse:
{ "epoch": 1704067200 }Recommended usage:
- Record the epoch value when you submit content.
- Periodically fetch the latest epoch.
- If the latest epoch is greater than your recorded value, re-submit the content.
Error handling
Expect standard HTTP status codes:
400for invalid requests401/403for authentication and authorization failures404for unknown endpoints or jobs500for server errors
Use retries for transient errors (5xx or network timeouts). Avoid retrying 4xx errors without correcting the request.
Not yet implemented
The following endpoints exist in the spec but are not currently supported:
POST /v1/status
If you need these, contact Freespoke support.
Client libraries
Support and onboarding
To integrate, you will need:
- An API key or OAuth2 client credentials (client ID and secret)
- Your publishing domains allow-listed
If you are ready to start or need help, contact support at [email protected].