Documentation
HTTP API
Send an image, get back a JSON report: each text region TIA found, its contrast ratio, and a WCAG verdict. No sign-in and no API key.
Endpoint
POST https://tia.dohunkim.xyz/api/audit
The body is multipart/form-data. The response is always JSON, with cache-control: no-store.
Request
| Field | Required | Description |
|---|---|---|
image | Yes | The image file: PNG, JPEG, or WebP, up to about 4 MB and 4 megapixels. Send it as a file part with an image content type. |
level | No | AA or AAA. Defaults to AA. Sets the ratio each region must reach. |
Examples
curl
curl -X POST https://tia.dohunkim.xyz/api/audit \
-F "image=@banner.png" \
-F "level=AA"Python (requests)
import requests
with open("banner.png", "rb") as f:
r = requests.post(
"https://tia.dohunkim.xyz/api/audit",
files={"image": ("banner.png", f, "image/png")},
data={"level": "AA"},
timeout=90,
)
if r.ok:
report = r.json()
for region in report["regions"]:
print(region["verdict"], region["contrast_ratio"], region["text"])
else:
print(r.status_code, r.json()["detail"])JavaScript (fetch)
// Browser or Node 18+. In the browser, take the file from an <input type="file">.
const form = new FormData();
form.append("image", file); // a File or Blob
form.append("level", "AA");
const res = await fetch("https://tia.dohunkim.xyz/api/audit", { method: "POST", body: form });
const body = await res.json();
if (!res.ok) throw new Error(body.detail);
console.log(body.summary); // { regions, passing, failing, review }The first request after a quiet period can take up to a minute while the service starts. Set a generous timeout (60 to 90 seconds) and retry once on a 503.
Response
A successful check returns 200 with this shape. The example is shortened (...) and its values are illustrative.
{
"image": { "width": 1200, "height": 630 },
"level": "AA",
"summary": { "regions": 3, "passing": 1, "failing": 1, "review": 1 },
"regions": [
{
"id": 0,
"location": { "x": 96, "y": 120, "width": 520, "height": 140 },
"text": "Spring sale starts today",
"verdict": "fail",
"contrast_ratio": 1.5,
"required_ratio": 3,
"text_size": "large",
"low_vision": [
{ "reader": "20/20", "reading": "difficult" },
{ "reader": "20/40", "reading": "unreadable" },
{ "reader": "20/80", "reading": "unreadable" },
{ "reader": "20/200", "reading": "unreadable" }
],
"guidance": "TIA reads this at about 1.5:1; WCAG AA needs 3:1 for large text. ..."
},
{
"id": 1,
"location": { "x": 96, "y": 300, "width": 610, "height": 36 },
"text": "Free delivery on every order this week",
"verdict": "pass",
"contrast_ratio": 12.7,
"required_ratio": 4.5,
"text_size": "normal",
"low_vision": [ ... ],
"guidance": "Meets WCAG AA contrast for normal text (12.7:1, needs 4.5:1 or more)."
},
{
"id": 2,
"location": { "x": 96, "y": 400, "width": 180, "height": 44 },
"text": "Shop the sale",
"verdict": "review",
"contrast_ratio": 4.9,
"required_ratio": 4.5,
"text_size": "normal",
"low_vision": null,
"guidance": "Couldn't measure this region confidently, so please check it by hand. ..."
}
],
"notes": [
"Each text region is checked against its own background.",
"..."
],
"provenance": {
"basis": "image-pixels",
"independent": true,
"disclosure": "Every color in this result was read from the uploaded image's own pixels; ..."
}
}Top-level fields
| Field | Type | Description |
|---|---|---|
image | object | width and height of the image as checked, in pixels. |
level | string | The level checked against: AA or AAA. |
summary | object | Counts: regions (text regions found), passing, failing, and review. They are counted from the verdicts in regions. |
regions | array | One object per text region, described below. Empty if no text was found. |
notes | array of strings | Plain-language notes about the whole result, such as what Review means and that this is not a certification. Show them to your users. |
provenance | object | Where the colors came from. For an uploaded image, basis is "image-pixels" and independent is true: every color was read from the image itself. disclosure says the same in a sentence. |
Region fields
| Field | Type | Description |
|---|---|---|
id | integer | The region’s index, from 0. The on-site report numbers regions from 1, so region 0 is shown as 1. |
location | object | The region’s box in image pixels: x and y of the top-left corner, then width and height. |
text | string or null | The text TIA read in the region, or null if it could not read it. Use it to find the region, not as an exact transcript. |
verdict | string | "pass", "fail", or "review". Review means TIA could not confirm a pass or a fail; check that region by hand. See Reading a report. |
contrast_ratio | number or null | The contrast ratio TIA measured between the text and its background, rounded to one decimal place. null when no ratio could be measured. |
required_ratio | number | The ratio WCAG asks for at this level and text size: 4.5 (normal) or 3 (large) at AA, 7 (normal) or 4.5 (large) at AAA. |
text_size | string | "normal" or "large", in the WCAG sense, as estimated from the image. An image does not say how large it will be shown, so treat this as an estimate. |
low_vision | array or null | How readable the region is likely to be for readers with different visual acuity. Each entry has reader ("20/20", "20/40", "20/80", or "20/200") and reading ("comfortable", "difficult", or "unreadable"). null when TIA could not estimate it. This is guidance beyond WCAG and does not change the verdict. |
guidance | string | One or two plain sentences on what the verdict means and what to do next. |
TIA is in beta, so new fields may be added. Ignore any field you do not recognize.
Errors
Every error is JSON with one field, detail: a short sentence you can show to a person. Branch on the status code, not on the wording.
| Status | When | detail |
|---|---|---|
| 400 | The body is not valid multipart form data | Invalid upload. |
| 400 | No image file part | No image provided. |
| 400 | level is not AA or AAA | level must be AA or AAA. |
| 400 | The file could not be opened as an image | Could not read that file as an image. |
| 413 | The request is over about 4.5 MB. The hosting platform refuses it before it reaches TIA, so the body is the platform’s error, not TIA’s JSON. | None |
| 413 | The image is over 4 megapixels | Image resolution too high; please downscale below 4 MP. |
| 415 | The file part does not have an image content type | Please upload an image file. |
| 429 | Too many requests from your address | Too many requests. Wait a minute and try again. |
| 500 | The service could not analyze the image, or the site is misconfigured | Could not analyze that image. or Server not configured. |
| 502 | The analysis service sent a response the site could not use | The analysis service returned an unexpected response. |
| 503 | The analysis service is starting or unreachable | The analysis service is starting up or unavailable. Please try again in a moment. or a similar sentence from the service itself |
If the service returns another error, the status code is passed through and detail is its short message, or a general sentence such as That request could not be processed. (4xx) or The analysis service had trouble with that image. Please try again. (5xx).
Limits
- File size: about 4 MB per image. The hosting platform refuses any request over 4.5 MB, so compress or downscale larger files first. For a larger image, use the MCP server with an image URL.
- Resolution: up to 4 megapixels (for example 2000 by 2000). Downscale larger images first.
- Rate: about 20 requests per 60 seconds per IP address. This limit is best effort: it is counted in memory on each server instance, so you may be cut off earlier or later than that. The analysis service has its own limit as well. On a
429, wait a minute and retry. - Speed: the first check after a quiet period can take up to a minute while the service starts. Later checks are usually much faster.
- Use: TIA is a free beta with no uptime promise. For large batches, send images one at a time.
Results are an automated aid, not legal advice or a certification of conformance. Before you act on them automatically, read where TIA can be wrong.