Free Public API

Public Generator API

Generate machine-readable terms-of-service permissions for any domain. No authentication required. No API keys. Just call and go.

GET https://api.openterms.com/generate?domain=example.com

Endpoint

The public API is hosted at api.openterms.com. There is one endpoint:

Parameter Type Description
domain string required Domain to look up or generate (e.g., stripe.com). URLs are accepted and stripped to hostname.
mode string optional authoritative_first (default) — checks whether the domain publishes its own openterms.json at /.well-known/openterms.json before generating. generate_always — skips the authoritative check and always runs the generator.
force boolean optional Set force=true to bypass the cache and regenerate fresh for this request. Counts against your rate limit.

Behavior

Default mode: authoritative_first

The API first checks whether the domain publishes its own openterms.json at /.well-known/openterms.json or /openterms.json. If found and valid JSON, that file is returned with meta.source = "authoritative". The generator is not run.

If no authoritative file is found, the API runs the OpenTerms generator — fetching the domain's Terms of Service, analyzing it with AI, and returning a generated openterms.json with meta.source = "generated".

Mode: generate_always

Skips the authoritative check. The generator always runs. Useful when you explicitly want AI analysis regardless of whether the domain publishes its own file.

Caching

Authoritative file lookups are cached for 1 hour per domain — short enough that site owners can publish their openterms.json and have it appear quickly. Generated results are cached for 24 hours per domain. Pass force=true to bypass the cache.

Responses

Authoritative file found

When the domain publishes its own openterms.json:

{
  "openterms_version": "0.3.0",
  "service": "stripe.com",
  "permissions": {
    "read_content": true,
    "scrape_data": false,
    "api_access": true,
    "allow_training": false
  },
  "meta": {
    "source": "authoritative",
    "source_url": "https://stripe.com/.well-known/openterms.json",
    "retrieved_at": "2026-04-17T15:00:00.000Z",
    "notice": "This is the domain's authoritative openterms.json file."
  }
}

Generator-produced file

When no authoritative file is found and the generator runs:

{
  "openterms_version": "0.3.0",
  "service": "example.com",
  "permissions": {
    "read_content": true,
    "scrape_data": null,
    "api_access": false,
    "create_account": true,
    "make_purchases": true,
    "post_content": false,
    "allow_training": false
  },
  "meta": {
    "source": "generated",
    "generator": "openterms.com",
    "generated_at": "2026-04-17T15:00:00.000Z",
    "generator_version": "0.3.1",
    "notice": "Generated by openterms.com. For authoritative permissions, contact the domain owner or publish your own openterms.json at /.well-known/openterms.json."
  }
}

Permission values: true = explicitly allowed, false = explicitly prohibited, null = not addressed in the terms.

Rate Limits

Limits are enforced per IP address:

  • 100 requests per hour
  • 1,000 requests per day

When a limit is hit, the API returns 429 Too Many Requests:

{
  "error": "rate_limit_exceeded",
  "retry_after_seconds": 1847,
  "limit_hit": "100/hour",
  "docs": "https://openterms.com/api"
}

Rate limit headers

Every response includes these headers:

  • X-RateLimit-Limit Hourly request limit (100)
  • X-RateLimit-Remaining Requests remaining in current hour window
  • X-RateLimit-Reset Unix timestamp when the hourly window resets
  • Retry-After Seconds to wait (only on 429 responses)

Error Responses

Status error code When
400 invalid_domain Missing or malformed domain parameter
404 not_found Domain is unreachable or has no discoverable terms of service
429 rate_limit_exceeded Rate limit hit. See retry_after_seconds and limit_hit in the response body
500 generator_failure Internal generator error. Retry in a moment
503 service_unavailable AI generation capacity reached. Includes Retry-After header

Error response body shape:

{ "error": "error_code", "message": "Human-readable description" }

Code Examples

curl — default (authoritative first)

curl "https://api.openterms.com/generate?domain=stripe.com"

curl — force regeneration, skip cache

curl "https://api.openterms.com/generate?domain=stripe.com&force=true"

curl — always generate (skip authoritative check)

curl "https://api.openterms.com/generate?domain=stripe.com&mode=generate_always"

Python — using requests

import requests

response = requests.get(
    "https://api.openterms.com/generate",
    params={"domain": "stripe.com"},
    timeout=60
)

if response.status_code == 200:
    data = response.json()
    print(f"Source: {data['meta']['source']}")
    print(f"scrape_data: {data['permissions'].get('scrape_data')}")
    print(f"allow_training: {data['permissions'].get('allow_training')}")
elif response.status_code == 429:
    retry = response.json().get("retry_after_seconds", 3600)
    print(f"Rate limited. Retry in {retry}s")
    print(f"Limit hit: {response.json().get('limit_hit')}")
elif response.status_code == 404:
    print("Domain not found or no terms discoverable")
else:
    print(f"Error {response.status_code}: {response.json()}")

Python — batch lookup

import requests, time

DOMAINS = ["stripe.com", "github.com", "openai.com", "reddit.com", "shopify.com"]
BASE = "https://api.openterms.com/generate"

for domain in DOMAINS:
    resp = requests.get(BASE, params={"domain": domain}, timeout=60)
    if resp.status_code == 429:
        retry = resp.json().get("retry_after_seconds", 60)
        print(f"Rate limited. Sleeping {retry}s...")
        time.sleep(retry)
        resp = requests.get(BASE, params={"domain": domain}, timeout=60)
    if resp.status_code == 200:
        d = resp.json()
        perms = d.get("permissions", {})
        print(f"{domain}: scrape={perms.get('scrape_data')}, "
              f"train={perms.get('allow_training')}, source={d['meta']['source']}")
    else:
        print(f"{domain}: error {resp.status_code}")

Python — using openterms-py SDK

The openterms-py SDK wraps the public API with retry logic and rate limit handling:

# pip install openterms-py  (currently in beta — see GitHub for install instructions)
from openterms import OpenTermsClient

client = OpenTermsClient()  # uses api.openterms.com by default

result = client.generate("stripe.com")
print(result.permissions.scrape_data)    # True / False / None
print(result.permissions.allow_training) # True / False / None
print(result.meta.source)               # "authoritative" or "generated"

Attribution Requirements

Required when using generated results in a product: If you display or use generated openterms.json data in your product, you must either preserve the meta.notice field in any output you expose, or include a visible attribution to openterms.com as the source. Authoritative files (published by the domain owner) carry no attribution requirement beyond accurately representing the source.

The meta.source field tells you whether data is "authoritative" (the domain owner published it) or "generated" (produced by OpenTerms AI analysis). This distinction matters: generated data is an AI interpretation of publicly available terms, not a legal certification.

Pricing & Access

Free public API — no authentication required.
No API keys. No sign-up. No billing. Just make requests. The rate limits (100/hour, 1000/day per IP) are sufficient for most integration and research use cases. If you need higher limits, contact us.

Open Source

The OpenTerms platform — including the generator, validator, registry, and this API — is hosted at github.com/jstibal/opentermsos. The registry seed dataset (500+ entries) is at github.com/jstibal/openterms-registry-seed.