Introduction
A simple JSON REST API to read your review sites, offers and articles, and to add new offers and articles programmatically. Everything you can do through the API you can also do in the dashboard — it writes the exact same data.
Base URL: https://cms.reviewtycoon.com/api/v1
| Method | Endpoint | Description |
|---|---|---|
| GET | /sites | List your review sites |
| GET | /sites/{id} | Get one review site |
| GET | /sites/{id}/categories | List categories on a site |
| POST | /sites/{id}/categories | Add a category |
| GET | /categories/{id} | Get one category |
| PATCH | /categories/{id} | Update a category |
| DELETE | /categories/{id} | Delete a category |
| GET | /sites/{id}/offers | List offers on a site |
| POST | /sites/{id}/offers | Add an offer |
| GET | /offers/{id} | Get one offer |
| PATCH | /offers/{id} | Update an offer |
| DELETE | /offers/{id} | Delete an offer |
| GET | /sites/{id}/articles | List articles on a site |
| POST | /sites/{id}/articles | Add an article |
| GET | /articles/{id} | Get one article |
| PATCH | /articles/{id} | Update an article |
| DELETE | /articles/{id} | Delete an article |
Authentication
Every request must include your API key as a Bearer token in the Authorization header:
Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Alternatively you may send it in an X-Api-Key header. All requests must use HTTPS.
Your key is tied to your account and grants access to only your own review sites,
offers and articles. Requests for data that doesn't belong to your account return 404;
a missing or wrong key returns 401.
Making requests
- Read endpoints use GET.
- Create endpoints use POST; updates use PATCH — both with a JSON body and the header
Content-Type: application/json. - Delete endpoints use DELETE.
- Request bodies must be valid JSON (max 1 MB).
curl https://cms.reviewtycoon.com/api/v1/sites \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Responses & errors
Successful responses wrap the result in a data field (list endpoints add meta):
{
"data": { ... },
"meta": { "page": 1, "per_page": 50, "total": 12, "total_pages": 1 }
}
Errors use a consistent shape and the matching HTTP status code:
{
"error": {
"code": "validation_error",
"message": "Validation failed.",
"details": { "fields": { "name": "This field is required." } }
}
}
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_json | Body is not valid JSON. |
| 401 | missing_api_key / invalid_api_key | No key, or an unknown key. |
| 403 | account_blocked | The account is blocked. |
| 404 | site_not_found / offer_not_found / article_not_found | Unknown resource, or not yours. |
| 405 | method_not_allowed | Wrong HTTP method for this endpoint. |
| 409 | duplicate_offer / duplicate_category | An offer/category with that name already exists on the site. |
| 409 | category_not_empty | Category still has offers/articles — reassign them first (see below). |
| 413 | payload_too_large | Body exceeds 1 MB. |
| 415 | unsupported_media_type | Body wasn't sent as application/json. |
| 422 | validation_error | One or more fields are invalid (see details.fields). |
| 429 | rate_limited | Too many requests — slow down. |
| 500 | internal_error | Something went wrong on our side. |
Rate limiting
Up to 120 requests per minute per account. Every response carries
X-RateLimit-Limit and X-RateLimit-Remaining headers; exceeding the
limit returns 429 with a retry_after (seconds) in the error details.
Pagination
List endpoints accept ?page= (default 1) and ?per_page=
(default 50, max 200). The meta object reports total and total_pages.
Updating
Update a single offer, article or category with PATCH. Updates are
partial: send only the fields you want to change — everything else stays as it
is. The accepted fields are the same as for the matching POST, plus
an optional slug (changing it registers a 301 redirect from the old URL). A
successful update returns 200 with the full updated resource.
# set an offer inactive and bump its rating — nothing else changes
curl -X PATCH "https://cms.reviewtycoon.com/api/v1/offers/12345" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "status": "inactive", "rating_editor": 9.2 }'
Deleting
DELETE is available for single offers, articles and categories.
Offers and articles are soft-deleted (hidden from your live site and from the
API, but recoverable by support if needed). Categories are removed permanently. A successful
delete returns 200 with { "data": { "id": …, "deleted": true } };
deleting an unknown/already-deleted resource returns 404.
Review sites
GET /sites
List all your review sites.
curl "https://cms.reviewtycoon.com/api/v1/sites" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{
"data": [
{
"id": 8,
"domain": "saastoolsreviews.com",
"url": "https://www.saastoolsreviews.com",
"type": "review",
"is_active": true,
"dns_active": true,
"created_at": "2018-10-10T12:54:01+02:00"
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "total_pages": 1 }
}
GET /sites/{id}
Fetch a single review site by id.
Categories
Every offer belongs to a category. Use these endpoints to look up the categories on a site
(to get a category_id for an offer) or to create a new one. You can also create a
category on the fly when adding an offer by passing category (a name) instead of
category_id — see Offers.
GET /sites/{id}/categories
List the categories on one of your sites. Supports pagination.
curl "https://cms.reviewtycoon.com/api/v1/sites/8/categories" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{
"data": [
{
"id": 7,
"site_id": 8,
"parent_id": null,
"name": "SEO Tools",
"slug": "seo-tools",
"offer_count": 12,
"article_count": 3,
"subcategory_count": 0,
"public_url": "https://www.saastoolsreviews.com/seo-tools/"
}
],
"meta": { "page": 1, "per_page": 50, "total": 1, "total_pages": 1 }
}
GET /categories/{id}
Fetch a single category (any of your sites).
POST /sites/{id}/categories
Add a new category to a site. Send a JSON body with these fields:
| Field | Type | Notes |
|---|---|---|
name | string | Required. Category name (max 255). Must be unique on the site. |
parent_id | int | Parent category on this site (omit for a top-level category). |
description | string | Description (HTML allowed). |
meta_title | string | SEO title (max 1000). |
meta_description | string | SEO description. |
sort_order | int | Manual sort position. |
curl "https://cms.reviewtycoon.com/api/v1/sites/8/categories" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "name": "Web Hosting", "description": "<p>Hosting providers.</p>" }'
# → 201 Created
{ "data": { "id": 42, "site_id": 8, "name": "Web Hosting",
"slug": "web-hosting", "parent_id": null,
"public_url": "https://www.saastoolsreviews.com/web-hosting/", ... } }
PATCH /categories/{id}
Update a category (partial). Fields: name, description,
meta_title, meta_description, parent_id,
sort_order, slug.
curl -X PATCH "https://cms.reviewtycoon.com/api/v1/categories/42" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "name": "Managed Hosting", "sort_order": 3 }'
DELETE /categories/{id}
Delete a category. By default only an empty category can be deleted; if it
still has offers or articles you get 409 category_not_empty (with
offer_count/article_count in the details). Pass the optional query
parameter ?move_to_category_id=<id> to move all its offers and articles to
another category on the same site first, then delete. Any subcategories become top-level.
# delete an empty category
curl -X DELETE "https://cms.reviewtycoon.com/api/v1/categories/42" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# or move its contents to category 7, then delete
curl -X DELETE "https://cms.reviewtycoon.com/api/v1/categories/42?move_to_category_id=7" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{ "data": { "id": 42, "deleted": true,
"moved_to_category_id": 7, "moved_offers": 3, "moved_articles": 1 } }
Offers
GET /sites/{id}/offers
List the offers on one of your sites. Optional filters ?status=active|inactive|all
(default all, excludes deleted) and ?category_id=<id> (only offers
in that category — an unknown category on this site returns 422). Supports pagination.
curl "https://cms.reviewtycoon.com/api/v1/sites/8/offers?status=active&category_id=42&per_page=25" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
GET /offers/{id}
Fetch a single offer (any of your sites).
POST /sites/{id}/offers
Add a new offer to a site. Send a JSON body with these fields:
category_id (an existing category — look it up via
Categories) or category (a name, which
is created or reused). Omitting both returns 422 validation_error.
| Field | Type | Notes |
|---|---|---|
name | string | Required. Offer name (max 100). Must be unique on the site. |
url | string | Required. Destination / fallback URL (http:// is added if missing). |
category_id | int | Required (this or category). An existing category on this site. |
category | string | Required (this or category_id). Name of a category to create/reuse (max 255). |
description | string | Description (HTML allowed). |
status | string | active (default) or inactive. |
label | string | Short label badge (max 100). |
price | string | Price text (max 25). |
summary | string | Short summary. |
meta_title | string | SEO title (max 1000). |
meta_description | string | SEO description. |
h1 | string | Page heading (defaults to the name). |
affiliate_url | string | Tracking / affiliate URL (max 255). |
affiliate_url_mobile | string | Mobile tracking URL. |
program_id | string | Affiliate program id (max 100). |
affiliate_network_id | int | Affiliate network id. |
ecpc | number | Estimated eCPC (0–32767). |
rating_editor | number | Editor rating 0–10 (1 decimal). |
is_top | bool | Mark as a top pick. |
is_spotlight | bool | Show in the spotlight. |
in_carousel | bool | Show in the carousel. |
is_popular | bool | Mark as popular. |
sort_order | int | Manual sort position. |
curl "https://cms.reviewtycoon.com/api/v1/sites/8/offers" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Hosting",
"url": "https://acme.example/go",
"category": "Web Hosting",
"description": "<p>Reliable hosting for small business.</p>",
"rating_editor": 8.5,
"is_top": true
}'
# → 201 Created
{ "data": { "id": 12345, "site_id": 8, "name": "Acme Hosting",
"slug": "acme-hosting", "status": "active",
"public_url": "https://www.saastoolsreviews.com/reviews/acme-hosting/", ... } }
PATCH /offers/{id}
Update an offer (partial). Accepts the same fields as
POST above (except category — use
category_id to move it), plus slug.
curl -X PATCH "https://cms.reviewtycoon.com/api/v1/offers/12345" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "affiliate_url": "https://acme.example/new-tracking", "is_top": true }'
DELETE /offers/{id}
Soft-delete an offer (hidden from your live site; recoverable by support).
curl -X DELETE "https://cms.reviewtycoon.com/api/v1/offers/12345" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{ "data": { "id": 12345, "deleted": true } }
Articles
GET /sites/{id}/articles
List the articles on one of your sites. Optional filters
?status=published|draft|all (default all) and
?category_id=<id> (only articles in that category — an unknown category on
this site returns 422). Supports pagination.
GET /articles/{id}
Fetch a single article (any of your sites).
POST /sites/{id}/articles
Add a new article to a site. Send a JSON body with these fields:
| Field | Type | Notes |
|---|---|---|
title | string | Required. Article title (max 255). |
content | string | Body (HTML allowed). |
subtitle | string | Subtitle. |
offer_id | int | Link the article to one of your offers. |
category_id | int | A category on this site. |
author_id | int | An author on your account. |
status | string | published (default) or draft. |
published_at | string | ISO-8601 date/time (defaults to now). |
keyword | string | Focus keyword (max 250). |
meta_title | string | SEO title (max 1000). |
meta_description | string | SEO description. |
h1 | string | Page heading. |
show_toc | bool | Show a table of contents. |
hide_picture | bool | Hide the featured image. |
curl "https://cms.reviewtycoon.com/api/v1/sites/8/articles" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"title": "Best hosting of 2026",
"content": "<p>Our full comparison…</p>",
"offer_id": 12345,
"status": "published"
}'
PATCH /articles/{id}
Update an article (partial). Accepts the same fields as
POST above, plus slug. Example: publish a draft.
curl -X PATCH "https://cms.reviewtycoon.com/api/v1/articles/6789" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "status": "published" }'
DELETE /articles/{id}
Soft-delete an article (hidden from your live site; recoverable by support).
curl -X DELETE "https://cms.reviewtycoon.com/api/v1/articles/6789" \
-H "Authorization: Bearer rt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{ "data": { "id": 6789, "deleted": true } }
ReviewTycoon API v1 · Get your API key