v1.0 · Live

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

Base URL https://categorizeapi.com/api

Key capabilities

Authentication

All requests must include a valid API key in the Authorization header using the Bearer scheme.

HTTP Header
Authorization: Bearer YOUR_API_KEY
🔑
Get your API key Create a free account at categorizeapi.com/signup — your API key is available immediately in the dashboard. Free tier includes 250 requests/month with no credit card required.

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

JSON Response · 200 OK
{
  "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.

POST /api/categorize

Categorizes a single URL or block of text and returns a structured JSON response.

POST /api/categorize-batch

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.

POST /api/batch-jobs

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.

GET /api/batch-jobs?id=<job_id>

Poll a batch job's status and (partial or final) results — useful whether or not you're also using webhook_url.

GET /api/taxonomies

List your saved custom taxonomies. Also manageable from the Taxonomies page in your dashboard.

POST /api/taxonomies

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.

PUT /api/taxonomies?id=<id>

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 /api/taxonomies?id=<id>

Delete a saved taxonomy.

GET /api/categories

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)

HeaderValueRequired
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.

ParameterTypeDefaultDescription
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 automatically
url — 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

JSON Request Body
{
  "input": "https://example.com/blog/electric-vehicles-2030",
  "type": "auto"
}
ℹ️
How URLs are fetchedFor 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 · 200 OK
{
  "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

FieldTypeDescription
successbooleanAlways true on success.
categorystringOne of the 25 top-level categories — see Categories.
category_idstringA 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.
subcategorystring | nullA 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_idstring | nullStable slug for subcategory, same scheme as category_id. null whenever subcategory is null.
confidencefloatModel confidence in the assigned category, from 0.0 to 1.0.
reasoningstringOne short sentence explaining why that category was chosen.
input_typestringEither url or text — how the input was actually interpreted.
processing_timefloatRequest duration in seconds.
requests_remainingintegerRemaining requests in the current billing period.
categoriesarrayPresent only when multi_label was true: [{category, category_id, confidence}, ...], ranked most-likely first.
translatedobjectPresent only when language was set: {language, category, subcategory, reasoning} localized into the requested language.
flagsobjectPresent 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).

#CategoryidExample 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
ℹ️
The default list is fixed — it won't change without a version bump. Build your integration against these exact strings (or the corresponding 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:

JSON Request Body — nested categories shape
{
  "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:

JSON Request Body — 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.

CodeLanguageCodeLanguage
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 Response
{
  "error": "Invalid API key"
}
StatusErrorDescription
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.

PlanRequests / monthAPI 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.

ℹ️
When you exceed your monthly limit, upgrade to a paid plan to continue using the API without interruption.

Changelog

2026-08
v1.0 Latest
  • 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.