API Reference
A simple REST API to categorize any URL or block of text in seconds. One endpoint, a fixed 25-category taxonomy, a confidence score.
Overview
CategorizeAPI provides a single HTTP endpoint that accepts a URL or a block of text and returns a category, an optional subcategory, a confidence score, and a short reasoning. The API is designed to be minimal and predictable — one endpoint, JSON in, JSON out.
Base URL
Key capabilities
- URL or text input — Send a link and we fetch the page server-side, or send text directly
- Auto-detection — Set
typetoautoand we'll detect which one you sent - Fixed taxonomy — 25 top-level categories, consistent across every request
- Confidence + reasoning — Every response includes a 0-1 confidence score and a one-line explanation
- Automatic failover — Backup infrastructure kicks in if the primary call fails
Authentication
All requests must include a valid API key in the Authorization header using the Bearer scheme.
Authorization: Bearer YOUR_API_KEY
API key format
API keys follow the format sk_live_xxxxxxxxxxxxxxxxxxxxxxxx (production) or sk_test_xxxxxxxxxxxxxxxxxxxxxxxx (test environment). Keep your key secret — treat it like a password.
Quick Start
Send a POST request to /api/categorize with a URL or text. You'll get a category back in a couple of seconds.
# Linux / macOS — line continuation with \ curl -X POST https://categorizeapi.com/api/categorize \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"input": "https://example.com", "type": "auto"}'
:: Windows CMD — use ^ for line continuation, \" inside double quotes curl -X POST https://categorizeapi.com/api/categorize ^ -H "Authorization: Bearer YOUR_API_KEY" ^ -H "Content-Type: application/json" ^ -d "{\"input\": \"https://example.com\", \"type\": \"auto\"}"
const categorize = async (input) => { const res = await fetch('https://categorizeapi.com/api/categorize', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ input, type: 'auto' }) }); const data = await res.json(); console.log(data.category, data.confidence); }; categorize('https://example.com');
import requests response = requests.post( "https://categorizeapi.com/api/categorize", headers={"Authorization": "Bearer YOUR_API_KEY"}, json={"input": "https://example.com", "type": "auto"} ) data = response.json() print(data["category"], data["confidence"])
<?php $response = file_get_contents('https://categorizeapi.com/api/categorize', false, stream_context_create(['http' => [ 'method' => 'POST', 'header' => "Authorization: Bearer YOUR_API_KEY\r\nContent-Type: application/json", 'content' => json_encode(['input' => 'https://example.com', 'type' => 'auto']) ]]) ); $data = json_decode($response, true); echo $data['category'];
Example response
{
"success": true,
"category": "Technology & Computing",
"category_id": "technology-computing",
"subcategory": "Software",
"subcategory_id": "software",
"confidence": 0.93,
"reasoning": "The page describes a software product and its documentation.",
"input_type": "url",
"processing_time": 1.14,
"requests_remaining": 4997
}
Endpoints
The main categorization call, a synchronous batch version, an async batch-job version for larger jobs, saved-taxonomy management, and a public taxonomy lookup.
Categorizes a single URL or block of text and returns a structured JSON response.
Categorizes up to 20 items in one call, synchronously. Body: {"items": [{"input": "...", "type": "auto"}, ...], ...shared options}. Any categories/language/multi_label/top_n/moderation/etc. params apply to every item. Each item counts as one request against your quota and your 60/minute burst limit — a partial failure (e.g. one bad URL) doesn't fail the whole batch, each result is reported individually.
For larger jobs: up to 500 items, processed asynchronously in the background. Same body shape as categorize-batch, plus an optional webhook_url (must be https://). Returns immediately with a job_id and a poll_url. If webhook_url is set, the final results are POSTed there when done, signed with X-CategorizeAPI-Signature: sha256=<hmac> (HMAC-SHA256 of the raw JSON body, keyed with your API key) so you can verify authenticity — recompute the same HMAC and compare.
Poll a batch job's status and (partial or final) results — useful whether or not you're also using webhook_url.
List your saved custom taxonomies. Also manageable from the Taxonomies page in your dashboard.
Save a taxonomy: {"name": "...", "categories": [...]} (2-50 entries, up to 20 saved per account). categories entries may be plain strings or {label, subcategories} objects, same as the categories request parameter. Returns an id — pass it as taxonomy_id to /api/categorize instead of resending the full categories array every call.
Edit a saved taxonomy in place. Body: {"name"?: "...", "categories"?: [...]} — both fields are optional, a partial update; omit one to leave it unchanged. Same response shape as POST.
Delete a saved taxonomy.
Public, unauthenticated. Returns the full default taxonomy (categories + example subcategories, each with a stable id) and the list of supported languages. Doesn't require an API key or count against your quota. Optional ?language=<code> query param returns the catalog itself — category and subcategory names — localized, rather than translating a classification result; covers en, fr, es, de, it, pt, nl, ar, zh, ja, ko, ru, other codes fall back to English. This is separate from the language parameter on POST /api/categorize, which translates a classification result via the model across all 34 supported languages.
Request headers (categorize / categorize-batch / batch-jobs / taxonomies)
| Header | Value | Required |
|---|---|---|
| Authorization | Bearer YOUR_API_KEY |
Required |
| Content-Type | application/json |
Required |
Request Parameters
Send a JSON body with the following fields. Only input is required.
| Parameter | Type | Default | Description |
|---|---|---|---|
| input required |
string | — | A URL (http:// or https://) or a block of text. Text input is capped at 7,500 characters. |
| type optional |
string | auto |
How to interpret input.auto — detect URL vs text automaticallyurl — force URL handling (errors if input isn't a valid URL)text — force plain-text handling, even if it looks like a URL
|
| categories optional |
string[] | object[] | default 25 | Bring your own taxonomy. 2-50 unique entries, each either a plain string (freeform subcategory, as before) or a {"label": "...", "subcategories": [...]} object — the two shapes can be mixed in the same array. Labels are up to 60 characters. When set, the model classifies into your list instead of the default 25 — category in the response will be one of your labels, verbatim. For any entry with a subcategories list (1-20 strings, each up to 60 characters), the model is constrained to pick exactly one of those verbatim for subcategory instead of guessing freeform text; entries without one stay freeform. |
| subcategories optional |
object | — | Add strict subcategory constraints without redefining the whole categories array: {"Category Label": ["Sub A", "Sub B"], ...}. Each key must match a label already in the active taxonomy — the default 25, a taxonomy_preset, or a taxonomy_id — so this composes with any of those instead of replacing them. Equivalent in effect to attaching a subcategories list to that category via the nested categories shape. |
| language optional |
string | en |
ISO 639-1 code from the supported languages list. Adds a translated object to the response with the category, subcategory, and reasoning localized into that language — the top-level category field always stays the canonical (English, or your custom list's) string so your matching logic never breaks. |
| multi_label optional |
boolean | false |
When true, adds a ranked categories array (see top_n) instead of a single guess — useful when content genuinely spans more than one topic. |
| top_n optional |
integer | 3 |
How many ranked categories to return when multi_label is true. 1-5. |
| moderation optional |
boolean | false |
When true, adds a flags object: {adult, violent, spam} booleans — content-safety signals from the same model call, no extra latency or cost. |
| taxonomy_preset optional |
string | — | Use a ready-made industry taxonomy instead of writing your own categories array: ecommerce, news_media, job_postings, saas_content, social_media, support_tickets, or content_moderation. |
| taxonomy_id optional |
integer | — | Use one of your own saved taxonomies (see /api/taxonomies) by ID, instead of resending its categories array on every call. Requires your own API key (not available via RapidAPI). |
| detect_language optional |
boolean | false |
When true, adds detected_language: {code, name} — the language the input content itself is written in (independent of the language param, which controls the output translation). |
| sentiment optional |
boolean | false |
When true, adds sentiment: {label, score} — label is one of positive/negative/neutral/mixed, score ranges -1.0 to 1.0. |
| extract_keywords optional |
boolean | false |
When true, adds keywords — an array of 5-8 key terms, topics, or named entities found in the content. |
Full request example
{
"input": "https://example.com/blog/electric-vehicles-2030",
"type": "auto"
}
type=url (or an auto-detected URL), the page is fetched server-side with an 8-second timeout, up to 3 redirects, and a ~300KB download cap. The title, meta description, and a content excerpt are extracted and classified — the raw HTML is never returned or stored.Response Format
All responses are JSON. A successful request returns HTTP 200.
{
"success": true,
"category": "News & Politics",
"category_id": "news-politics",
"subcategory": "World News",
"subcategory_id": "world-news",
"confidence": 0.87,
"reasoning": "The text reports on an international diplomatic event.",
"input_type": "text",
"processing_time": 0.92,
"requests_remaining": 4996
}
Response fields
| Field | Type | Description |
|---|---|---|
| success | boolean | Always true on success. |
| category | string | One of the 25 top-level categories — see Categories. |
| category_id | string | A stable, URL-safe slug for category (e.g. "News & Politics" → "news-politics"), deterministically derived from the label. Use this to match on an id that survives label wording or translation changes, instead of matching on the human-readable string. |
| subcategory | string | null | A more specific free-form label, when the model can be more precise. Not a fixed list — unless constrained via subcategories or a nested categories entry, in which case it's one of your strings, verbatim. |
| subcategory_id | string | null | Stable slug for subcategory, same scheme as category_id. null whenever subcategory is null. |
| confidence | float | Model confidence in the assigned category, from 0.0 to 1.0. |
| reasoning | string | One short sentence explaining why that category was chosen. |
| input_type | string | Either url or text — how the input was actually interpreted. |
| processing_time | float | Request duration in seconds. |
| requests_remaining | integer | Remaining requests in the current billing period. |
| categories | array | Present only when multi_label was true: [{category, category_id, confidence}, ...], ranked most-likely first. |
| translated | object | Present only when language was set: {language, category, subcategory, reasoning} localized into the requested language. |
| flags | object | Present only when moderation was true: {adult, violent, spam} booleans. |
Multi-label response example
{
"success": true,
"category": "Sports",
"category_id": "sports",
"confidence": 0.9,
"categories": [
{ "category": "Sports", "category_id": "sports", "confidence": 0.9 },
{ "category": "Technology & Computing", "category_id": "technology-computing", "confidence": 0.8 },
{ "category": "Health & Fitness", "category_id": "health-fitness", "confidence": 0.7 }
]
}
Multi-language response example (language: "fr")
{
"success": true,
"category": "Technology & Computing",
"category_id": "technology-computing",
"subcategory": "Consumer Electronics",
"subcategory_id": "consumer-electronics",
"translated": {
"language": "fr",
"category": "Technologie et informatique",
"subcategory": "Électronique grand public",
"reasoning": "Le contenu décrit la sortie d'un nouveau modèle d'ordinateur portable."
}
}
Categories & Languages
Every response's category field is exactly one of these 25 values, verbatim — unless you pass your own categories array (see Request Parameters), in which case it's one of yours instead. Every response also carries a category_id, a stable slug derived from the label (and a subcategory_id when subcategory is set) — see Response Format. The full list, ids included, is also available live at GET /api/categories (no API key required).
| # | Category | id | Example subcategories |
|---|---|---|---|
| 1 | Arts & Entertainment |
arts-entertainment |
Movies, Music, TV Shows, Celebrity News, Books & Literature, Theater |
| 2 | Automotive |
automotive |
Car Reviews, Electric Vehicles, Motorcycles, Auto Repair, Car Buying, Racing |
| 3 | Business & Industrial |
business-industrial |
Startups, Manufacturing, Marketing, Human Resources, Logistics, B2B Services |
| 4 | Careers |
careers |
Job Search, Resume & Interviews, Remote Work, Career Advice, Freelancing |
| 5 | Education |
education |
Online Courses, K-12, Higher Education, Test Prep, Language Learning |
| 6 | Family & Parenting |
family-parenting |
Parenting Tips, Pregnancy, Childcare, Family Activities |
| 7 | Finance & Investing |
finance-investing |
Personal Finance, Stock Market, Cryptocurrency, Banking, Insurance, Retirement |
| 8 | Food & Drink |
food-drink |
Recipes, Restaurant Reviews, Nutrition, Beverages, Cooking Tips |
| 9 | Gaming |
gaming |
Video Game Reviews, Esports, Mobile Gaming, Game Guides, Board Games |
| 10 | Health & Fitness |
health-fitness |
Exercise & Workouts, Mental Health, Nutrition, Medical Conditions, Wellness |
| 11 | Hobbies & Interests |
hobbies-interests |
Photography, Crafts & DIY, Collecting, Outdoor Activities |
| 12 | Home & Garden |
home-garden |
Home Improvement, Interior Design, Gardening, Home Appliances |
| 13 | Law & Government |
law-government |
Legal Advice, Government Services, Public Policy, Immigration |
| 14 | News & Politics |
news-politics |
Breaking News, World Politics, Local News, Elections, Opinion & Editorial |
| 15 | Pets & Animals |
pets-animals |
Dogs, Cats, Pet Care, Wildlife |
| 16 | Real Estate |
real-estate |
Home Buying, Rental Listings, Commercial Real Estate, Mortgages |
| 17 | Science |
science |
Space & Astronomy, Environment & Climate, Physics, Biology, Research |
| 18 | Shopping & Retail |
shopping-retail |
Product Reviews, Deals & Coupons, E-commerce, Comparison Shopping |
| 19 | Society & Culture |
society-culture |
Social Issues, History, Philosophy, Cultural Commentary |
| 20 | Sports |
sports |
General Sports News, Football/Soccer, Basketball, Baseball, Fantasy Sports |
| 21 | Style & Fashion |
style-fashion |
Clothing & Apparel, Beauty, Accessories, Fashion Trends |
| 22 | Technology & Computing |
technology-computing |
Software, Hardware Reviews, Artificial Intelligence, Cybersecurity, Mobile Tech |
| 23 | Travel & Tourism |
travel-tourism |
Destination Guides, Hotels & Lodging, Flights, Travel Tips, Adventure Travel |
| 24 | Religion & Spirituality |
religion-spirituality |
Christianity, Islam, Buddhism, Meditation, Spirituality |
| 25 | Adult Content |
adult-content |
Adult Content |
ids), or supply your own via categories.Custom taxonomy with strict subcategories
Both the nested categories shape and the standalone subcategories param constrain subcategory to a fixed list, verbatim, instead of letting the model guess freeform text. Mix and match plain strings with {label, subcategories} objects in the same categories array:
{
"input": "Our new noise-cancelling headphones ship next month, starting at $199.",
"categories": [
{ "label": "Electronics", "subcategories": ["Audio", "Wearables", "Computers"] },
"Apparel",
"Home Goods"
]
}
Here, if the model chooses "Electronics", subcategory is forced to be exactly one of "Audio"/"Wearables"/"Computers"; "Apparel" and "Home Goods" stay freeform. Equivalently, without redefining categories at all, you can layer the same constraint onto the default 25 (or a taxonomy_preset) with the standalone subcategories param:
{
"input": "Our new noise-cancelling headphones ship next month, starting at $199.",
"subcategories": {
"Shopping & Retail": ["Product Reviews", "Deals & Coupons", "E-commerce", "Comparison Shopping"]
}
}
The key must match a label already in whatever taxonomy is active for the call — the default 25 unless overridden by categories, taxonomy_preset, or taxonomy_id. Saved taxonomies (/api/taxonomies) can store the nested {label, subcategories} shape too, and you can browse or build one interactively from the Taxonomies dashboard page.
Supported languages (language parameter)
Pass any of these ISO 639-1 codes to get translated output. English (en) is the default and requires no parameter.
| Code | Language | Code | Language |
|---|---|---|---|
en |
English | ja |
Japanese |
es |
Spanish | ko |
Korean |
fr |
French | vi |
Vietnamese |
de |
German | th |
Thai |
it |
Italian | id |
Indonesian |
pt |
Portuguese | ms |
Malay |
nl |
Dutch | tl |
Filipino |
ru |
Russian | sv |
Swedish |
pl |
Polish | no |
Norwegian |
tr |
Turkish | da |
Danish |
ar |
Arabic | fi |
Finnish |
he |
Hebrew | el |
Greek |
hi |
Hindi | cs |
Czech |
bn |
Bengali | ro |
Romanian |
ur |
Urdu | hu |
Hungarian |
fa |
Persian | uk |
Ukrainian |
zh |
Chinese | sw |
Swahili |
Errors
Error responses include an error field describing what went wrong. HTTP status codes follow standard conventions.
{
"error": "Invalid API key"
}
| Status | Error | Description |
|---|---|---|
| 400 | Invalid parameters | Missing input, type not one of auto/url/text, text exceeds the character limit, or malformed JSON body. |
| 401 | Unauthorized | Missing, expired, or invalid API key. Check the Authorization header. |
| 405 | Method not allowed | Only POST requests are accepted. |
| 422 | URL fetch failed | type=url was requested but the page could not be fetched — timed out, returned a non-2xx/3xx status, or had no extractable content. |
| 429 | Rate limit exceeded | Monthly request quota or the 60 req/min burst limit was reached. Upgrade your plan or slow down. |
| 500 | Server error | The categorization engine failed after all retries. Retry with exponential backoff. If the problem persists, contact support. |
cURL Examples
Complete examples using curl — available on Linux, macOS, and Windows.
Categorize a URL
curl -X POST https://categorizeapi.com/api/categorize \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": "https://www.espn.com", "type": "url" }'
curl -X POST https://categorizeapi.com/api/categorize ^ -H "Authorization: Bearer YOUR_API_KEY" ^ -H "Content-Type: application/json" ^ -d "{\"input\": \"https://www.espn.com\", \"type\": \"url\"}"
Categorize raw text
curl -X POST https://categorizeapi.com/api/categorize \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "input": "The Federal Reserve announced a quarter-point rate cut today, citing cooling inflation and a softening labor market.", "type": "text" }'
curl -X POST https://categorizeapi.com/api/categorize ^ -H "Authorization: Bearer YOUR_API_KEY" ^ -H "Content-Type: application/json" ^ -d "{\"input\": \"The Federal Reserve announced a quarter-point rate cut today.\", \"type\": \"text\"}"
JavaScript Examples
Works in Node.js 18+ (built-in fetch) and all modern browsers.
class CategorizeAPIClient { constructor(apiKey, baseURL = 'https://categorizeapi.com/api') { this.apiKey = apiKey; this.baseURL = baseURL; } async categorize(input, type = 'auto') { const res = await fetch(`${this.baseURL}/categorize`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ input, type }) }); if (!res.ok) { const err = await res.json(); throw new Error(err.error || 'API error'); } return res.json(); } } // Usage const client = new CategorizeAPIClient('YOUR_API_KEY'); const result = await client.categorize('https://example.com'); console.log('Category:', result.category); console.log('Confidence:', Math.round(result.confidence * 100) + '%');
// npm install axios const axios = require('axios'); const client = axios.create({ baseURL: 'https://categorizeapi.com/api', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }); async function categorize(input, type = 'auto') { const { data } = await client.post('/categorize', { input, type }); return data; } categorize('https://example.com') .then(r => console.log(r.category, r.confidence)) .catch(e => console.error(e.response?.data?.error || e.message));
Python Examples
Works with Python 3.7+. Uses the requests library (pip install requests).
import requests class CategorizeAPIClient: def __init__(self, api_key: str, base_url: str = "https://categorizeapi.com/api"): self.api_key = api_key self.base_url = base_url def categorize(self, input: str, type: str = "auto") -> dict: response = requests.post( f"{self.base_url}/categorize", headers={"Authorization": f"Bearer {self.api_key}"}, json={"input": input, "type": type}, timeout=30 ) response.raise_for_status() return response.json() # Usage client = CategorizeAPIClient("YOUR_API_KEY") result = client.categorize("https://example.com") print(f"Category: {result['category']}") print(f"Confidence: {round(result['confidence'] * 100)}%")
import aiohttp import asyncio async def categorize(api_key: str, input: str, type: str = "auto") -> dict: async with aiohttp.ClientSession() as session: async with session.post( "https://categorizeapi.com/api/categorize", headers={"Authorization": f"Bearer {api_key}"}, json={"input": input, "type": type} ) as resp: return await resp.json() # Usage result = asyncio.run(categorize( "YOUR_API_KEY", "https://example.com" )) print(result["category"])
PHP Examples
Works with PHP 7.4+. Uses cURL (enabled by default in most PHP installations).
<?php class CategorizeAPIClient { private string $apiKey; private string $baseUrl = 'https://categorizeapi.com/api'; public function __construct(string $apiKey) { $this->apiKey = $apiKey; } public function categorize(string $input, string $type = 'auto'): array { $payload = json_encode(['input' => $input, 'type' => $type]); $ch = curl_init($this->baseUrl . '/categorize'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $this->apiKey, 'Content-Type: application/json' ], CURLOPT_TIMEOUT => 30 ]); $body = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { $err = json_decode($body, true); throw new \RuntimeException($err['error'] ?? 'API error'); } return json_decode($body, true); } } // Usage $client = new CategorizeAPIClient('YOUR_API_KEY'); $result = $client->categorize('https://example.com'); echo $result['category'] . PHP_EOL; echo $result['confidence'] . PHP_EOL;
<?php // composer require guzzlehttp/guzzle use GuzzleHttp\Client; $client = new Client([ 'base_uri' => 'https://categorizeapi.com/api/', 'headers' => ['Authorization' => 'Bearer YOUR_API_KEY'] ]); $response = $client->post('categorize', [ 'json' => [ 'input' => 'https://example.com', 'type' => 'auto' ] ]); $data = json_decode($response->getBody(), true); echo $data['category'];
Rate Limits
Monthly request limits are enforced per calendar month and reset on the 1st of each month. A separate burst limit of 60 requests/minute applies to every plan.
| Plan | Requests / month | API access |
|---|---|---|
| Free | 250 | Full API access |
| Professional — €5/mo | 5,000 | Full API access + priority processing |
| Growth — €9/mo | 50,000 | Full API access + priority processing + 90-day history |
| Business — €19/mo | 1,000,000 | Full API access + dedicated support + SLA 99.9% |
When you exceed your monthly limit, the API returns HTTP 429. The requests_remaining field in every successful response lets you track usage programmatically.
Changelog
- Initial public release
- REST API with JSON request/response
- URL and text input, auto-detected
- 25-category fixed taxonomy with confidence + reasoning
- Automatic failover to backup infrastructure
- API key authentication
- Free tier — 250 requests/month
Have a feature request or found a bug? Contact us — we read every message.