Free AI Detector API and Widget
Put the WrittenCheck AI detector on your own site with one line of code, or send text to our JSON API and get a score back. Both are free with a free WrittenCheck account.
Built for teachers' sites, school portals, blogs and writing tools.
Build your widget
Change the settings and the live preview updates as you go. To copy the code, sign in and pick your widget key. A widget key only works on the websites you list.
Live preview (the real widget, try it here)
Recommended. Paste it where the detector should appear. It takes the full width of that spot.
For sites that don't allow scripts. It looks and works the same as the script tag.
Turn on "Send results to your page" above, then add this after the widget. Your page gets the score and verdict. It never gets the text.
How to add it
- Create a free account and confirm your email, or sign in.
- Create a widget key and list the websites where the widget will appear. Do it in the builder above, or on the Widgets page of your account.
- Choose your options in the builder, then copy the script tag. Use the iframe code instead if your site blocks scripts.
- Paste it into your page where the detector should appear, then save and publish.
Notes for common platforms. We tested plain HTML. The rest should work in the way described, but we haven't tested them yet.
- Plain HTML (tested): paste the code inside the
<body>of your page. - WordPress (should work, not tested yet): add a Custom HTML block and paste the code. Some security plugins strip scripts, so use the iframe code if the widget doesn't show.
- Google Sites (should work, not tested yet): Insert, then Embed, then Embed code. Paste the iframe code.
- Wix and Squarespace (should work, not tested yet): add an HTML embed or Code block and paste either code.
- Blogger (should work, not tested yet): switch the post to HTML view and paste the code.
Widget keys. The key in the code isn't secret. When the widget loads, our server checks which website shows it and only loads it on the websites listed on that key. On any other site it shows a short notice instead. Add example.com and it also works on www.example.com. Add *.example.com for every subdomain, and localhost for testing. Widgets added without a key keep working until 11 November 2026.
The widget works best with a wide spot on your page, such as the main content column. You can put several widgets on one page. Each gets its own box and its own options.
REST API
Send text to one address and get a JSON result. Call it from your server with your free API key. The API is for websites, and the page that uses it shows a link to WrittenCheck.
POST https://writtencheck.com/api/v1/detect
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{"text": "...", "language": "auto", "sentences": true}API keys
- Create a free account with Google or your email, and confirm the email.
- Open API keys, press New key and enter the web page where you will use the API. Copy the key right away. We only show it once.
- Show a visible link to WrittenCheck on that page, near the tool:
AI detection by <a href="https://writtencheck.com">WrittenCheck</a>. Anofollowlink is fine. - Send it with every call, as
Authorization: Bearer YOUR_API_KEYorX-API-Key: YOUR_API_KEY.
Your account page shows every call, day by day, with errors and results, so you can see how your integration behaves. Keep keys on your server. Don't put them in a web page or an app where people can read them. If one leaks, revoke it and make a new one.
The credit link. We check the page when you create the key and then automatically: every few hours while the key is new, and every few days after that. You don't need to press anything once the link is up. A new key works for 7 days while you add the link, and we email a reminder 2 days before the end. If the page still has no visible link after the 7 days, the key is revoked automatically and your account and that website can't get a new key for 7 days. If a link that was there disappears, we email you and the key keeps working for 7 more days. Hidden links, links with no text and pages that show the link only to our checker don't count. Every call needs a key. A call without one gets 401 api_key_required. Don't want to write code or add a link? The widget has the credit built in.
Request fields
| Field | Required | What it does |
|---|---|---|
text | Yes | The text to check, 40 to 3,000 words. Words are counted the same way as on our site. In Chinese, Japanese and Korean one character counts as one word. In Thai, about three characters count as one word. |
language | No | auto (the default) or one of en, es, pt, fr, de, it, ru, ja, ko, zh, ar, hi, id, tl (Tagalog), tr, th, ur. |
sentences | No | true (the default) adds a score for each sentence. Send false for a smaller reply. |
The body must be JSON, sent with Content-Type: application/json, and no bigger than 100 KB.
Response
{
"ok": true,
"version": "v1",
"score": 87,
"range": {"low": 74, "high": 96},
"confidence": "moderate",
"verdict": "likely_ai",
"language": "en",
"words": 212,
"sentences": [{"text": "...", "score": 91}],
"note": "Scores are estimates, not proof of authorship."
}This shows the shape of a reply. The examples further down show real replies.
| Field | Meaning |
|---|---|
score | 0 to 100. Higher means the writing reads more like AI. It isn't the share of the text written by AI. |
range | Where the score probably sits. Short texts get a wider range. |
confidence | low, moderate or high. Texts under 80 words always get low. |
verdict | likely_ai for a score of 70 or more, mixed for 45 to 69 and likely_human under 45. |
language | The language the detector used. |
words | How many words were checked. |
sentences | Each sentence with its own score. Missing when you send "sentences": false. |
engine | neural when our neural detector answered, or builtin when our rules engine did. Calls with a key go to the neural detector first, and the rules engine answers if it is busy or offline. |
notice | Only while a key is in its setup week and we haven't found the WrittenCheck link yet. A reminder to add it. |
Errors
Every error has the same shape: {"ok": false, "error": "code", "message": "plain words"}.
| Status | Error code | When |
|---|---|---|
| 400 | invalid_json, missing_text, invalid_language | The body isn't a JSON object, text is missing or empty, or language isn't on the list. |
| 401 | invalid_key, api_key_required | The key is wrong or was revoked, or no key was sent. |
| 403 | credit_required, key_revoked_credit | The page for this key doesn't show a link to WrittenCheck and the setup week is over, or the key was already revoked for that reason. |
| 405 | method_not_allowed | Anything other than POST (or an OPTIONS preflight). |
| 413 | too_long | Over 3,000 words, or a body over 100 KB. |
| 415 | unsupported_type | The body wasn't sent as application/json. |
| 422 | insufficient_text | Fewer than 40 words. The reply includes minimum_words. |
| 422 | unsupported_text | The text couldn't be scored, for example a list of numbers. |
| 429 | rate_limited | Too many checks for your account. Wait for the seconds in the Retry-After header. |
| 503 | busy | The detector is busy, or the shared daily limit is used up. Retry-After says when to try again. |
Each reply also carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (a Unix time). With a key, the limit shown is your account's daily limit. The API answers the CORS preflight and sets no cookies.
Examples
Replace YOUR_API_KEY with your own key. The outputs below are real replies from the live API on 4 October 2026, copied unedited, from before keys were added. A call with a key today returns the same fields plus engine. We ran the JavaScript example in a browser. For curl, Python and PHP we sent the same request from a browser too, because the reply is the same whichever tool sends it. The human text is the opening of Pride and Prejudice by Jane Austen (1813). The AI text was written by an AI model for this page. Both are short, so the confidence is low.
curl
curl -s https://writtencheck.com/api/v1/detect \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife. However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.", "sentences": false}'Output
{"ok":true,"version":"v1","score":0,"range":{"low":0,"high":20},"confidence":"low","verdict":"likely_human","language":"en","words":70,"note":"Scores are estimates, not proof of authorship."}JavaScript (fetch)
const res = await fetch("https://writtencheck.com/api/v1/detect", {
method: "POST",
headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json" },
body: JSON.stringify({
text: "Remote work has reshaped how teams collaborate, communicate and measure success. By embracing flexible schedules and digital tools, organizations can tap into a wider talent pool while helping employees achieve a healthier work-life balance. However, this shift also requires clear expectations, intentional communication and a strong culture of trust to keep everyone aligned and engaged.",
sentences: false
})
});
const data = await res.json();
console.log(res.status, JSON.stringify(data));Output
200 {"ok":true,"version":"v1","score":100,"range":{"low":80,"high":100},"confidence":"low","verdict":"likely_ai","language":"en","words":55,"note":"Scores are estimates, not proof of authorship."}Python (requests)
import requests
text = ("It is a truth universally acknowledged, that a single man in possession of a good fortune, "
"must be in want of a wife. However little known the feelings or views of such a man may be on "
"his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding "
"families, that he is considered the rightful property of some one or other of their daughters.")
r = requests.post("https://writtencheck.com/api/v1/detect", json={"text": text},
headers={"Authorization": "Bearer YOUR_API_KEY"})
print(r.status_code)
print(r.text)Output
200
{"ok":true,"version":"v1","score":0,"range":{"low":0,"high":20},"confidence":"low","verdict":"likely_human","language":"en","words":70,"sentences":[{"text":"It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife.","score":6},{"text":"However little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters.","score":2}],"note":"Scores are estimates, not proof of authorship."}PHP (curl)
<?php
$text = "Remote work has reshaped how teams collaborate, communicate and measure success. By embracing flexible schedules and digital tools, organizations can tap into a wider talent pool while helping employees achieve a healthier work-life balance. However, this shift also requires clear expectations, intentional communication and a strong culture of trust to keep everyone aligned and engaged.";
$ch = curl_init("https://writtencheck.com/api/v1/detect");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer YOUR_API_KEY", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["text" => $text, "sentences" => false]),
CURLOPT_RETURNTRANSFER => true,
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), "\n", $body, "\n";Output
200
{"ok":true,"version":"v1","score":100,"range":{"low":80,"high":100},"confidence":"low","verdict":"likely_ai","language":"en","words":55,"note":"Scores are estimates, not proof of authorship."}A text that's too short
curl -s https://writtencheck.com/api/v1/detect \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "This sentence is much too short to check."}'Output
{"ok":false,"error":"insufficient_text","message":"Send at least 40 words. Short texts don't give the detector enough to go on.","minimum_words":40,"words":8}Limits and fair use
The widget and the API are free. API calls are limited per account. The widget has per-IP limits. These are the numbers in our code today.
| Limit | Widget | REST API |
|---|---|---|
| Checks with an API key | Not needed | 1,000 a day and 60 a minute per account, shared by all your keys. Resets at midnight UTC. |
| Checks per minute, per IP address | 8 | Not used, limits are per account |
| Checks per day, per IP address | 200. Resets at midnight UTC. | Not used, limits are per account |
| Words per check | 40 to 3,000 (you can set a lower cap) | 40 to 3,000 |
| Request size | Set by the widget | 100 KB |
| Checks per day, whole site | 5,000 across all widgets. After that, they answer "busy" until midnight UTC. | Separate capacity |
| Checks running at once | 2 | 3. Our own site has separate capacity. |
With the widget, each visitor to your site counts against their own IP address. With an API key, calls count against your account wherever they come from. If you need more, email writtenchecksupport@gmail.com and tell us about your project. Please don't try to get round the limits.
Privacy
- Text sent through the widget or the API is checked in memory and then thrown away. We don't store it, log it or train on it.
- For each check we keep a short record without the text: the time, country, language, word count, score, verdict and which detector answered. Calls with a key are linked to your account so you can see your usage. We don't record which site a widget check came from.
- The widget sets no cookies, uses no browser storage and loads nothing from other companies.
- Your page only gets results if you turn on events, and then it gets the score and verdict, never the text.
- Results are estimates. Don't use a score as the only reason to decide anything about a person. Our privacy policy and terms cover the widget and the API.
Common questions
Is the AI detector API free?
Yes. The widget and the REST API are both free within the limits on this page. We don't show ads in the widget and we don't charge for checks.
Do I need an API key?
For the REST API, yes, on every call. Keys are free: create an account, then make a key on the API keys page and show a link to WrittenCheck on the page that uses it. The widget uses a separate widget key, made the same way. If you need higher limits, email us and tell us about your project.
What languages does it support?
The detector checks text in 17 languages: English, Spanish, Portuguese, French, German, Italian, Russian, Japanese, Korean, Chinese, Arabic, Hindi, Indonesian, Tagalog (Filipino), Turkish, Thai and Urdu. The widget's buttons and messages can show in all of these except Tagalog.
Can I change the widget's colours and layout?
Yes. You can set the theme, accent colour, corners, font, background, border, which result sections show, the sample buttons, a heading, the placeholder, the button label, the word limit and the height. If your accent colour is hard to read, the widget darkens or lightens it so text stays readable.
Will the widget slow down my site?
The loader script is small and loads with async, and the detector loads lazily in an iframe when it comes into view. It makes no requests to other companies.
Is my visitors' text stored?
No. Text is checked in memory and thrown away when the check ends. It isn't logged, stored or used for training, and your page never receives it.
Can I use the score to grade students?
Not on its own. A score is an estimate of writing patterns, not proof of who wrote a text. Human writing is sometimes flagged, especially short or very formal text. Treat a high score as a reason to look closer and talk to the writer.
What happens if the limit is reached?
The widget shows a plain message saying when to try again, and the API returns status 429 or 503 with a Retry-After header. Account limits, per-IP limits and the shared daily limit all reset at midnight UTC.
Changelog
- v1.5, 14 October 2026: credit checks run automatically. Keys still without the link after 7 days are revoked, with a 7-day wait before a new key.
- v1.4, 14 October 2026: every API call needs a key, and each key belongs to one web page that shows a link to WrittenCheck. New keys get 7 days to add the link.
- v1.3, 13 October 2026: the widget code now needs a free widget key, which only works on the websites you list. Widgets added without a key keep working until 11 November 2026, then show a notice.
- v1.2, 12 October 2026: free API keys with per-account limits and a usage page. Calls with a key go to our neural detector first and return an
enginefield. Calls without a key work until 11 November 2026. - v1.1, 4 October 2026: the widget now uses the same two-panel design as the detector on writtencheck.com, with the result shown next to the text.
- v1, launched 4 October 2026.