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