Docs
One script tag and one function, onetag.ask. The same reference, written for AI coding tools, is at llms.txt.
Install
Get a key, then add two script tags to your page:
<script src="https://onetagai.com/v1.js" data-key="YOUR_ONETAG_KEY"></script>
<script type="module">
const summary = await onetag.ask("Summarize in 3 bullets", document.body);
</script>
The SDK tag is a classic script, so onetag is ready before any module script runs. Top-level await only works inside <script type="module">, so put your calls there (or inside an async function).
The SDK is open source (github.com/fengyiqicoder/onetagai). You can also load it from jsDelivr, https://cdn.jsdelivr.net/gh/fengyiqicoder/onetagai@main/sdk/v1.js, or copy v1.js into your own site; it talks to https://onetagai.com/v1/ask either way.
onetag.ask
onetag.ask(prompt, input?, options?) → Promise<string | object>
prompt is the instruction. input is what it works on. The promise resolves with the answer as text, or as a parsed object when you pass json: true.
Input
| You pass | OneTag AI sends |
|---|---|
| a string | the text |
| an HTML element | its innerText |
a <form> | each field as a name: value line, except password fields |
<input>, <textarea>, <select> | its .value |
an <img> | the image, downscaled to a 1,600 px JPEG (a cross-origin image needs crossorigin="anonymous") |
an image File or Blob | the image, downscaled the same way |
| a plain object | it as JSON |
an array, NodeList or other iterable of any of these | all of them, in order |
Options
| Option | Type | Default | What it does |
|---|---|---|---|
stream | function | none | Called with each piece of text as it arrives: stream(delta). The promise still resolves with the full text. |
json | boolean | false | Asks for a JSON object and resolves with it parsed. Throws bad_json if the answer is not valid JSON. |
system | string | none | Extra instructions for the model: a persona, a tone, rules. |
maxTokens | number | 500 | Maximum length of the answer in tokens, up to 1000. |
history | array | none | Earlier turns of a chat: [{ role: "user" | "assistant", content: "..." }]. |
signal | AbortSignal | none | Cancels the request, streaming included, when it fires: pass controller.signal from an AbortController. The promise rejects with aborted. |
<script type="module">
const out = document.querySelector("#answer");
await onetag.ask("Explain this table to a beginner", document.querySelector("table"), {
stream: (delta) => { out.textContent += delta; },
maxTokens: 300,
});
</script>
onetag.configure
onetag.configure({ key: "YOUR_ONETAG_KEY", endpoint: "https://onetagai.com/v1/ask" });
Sets the key in code instead of the data-key attribute. endpoint defaults to /v1/ask on the server the SDK was loaded from; you only need it to point the SDK somewhere else.
Handling errors
Every failure throws onetag.OneTagError with .code (from the table below), .message (a sentence you can show to people) and .status (the HTTP status, or 0 when there was no response).
<script type="module">
const out = document.querySelector("#out");
try {
out.textContent = await onetag.ask("Translate to French", document.querySelector("#text"));
} catch (e) {
out.textContent = e.code === "quota_exceeded" ? "The AI is taking a break." : e.message;
}
</script>
Error codes
| Code | HTTP | Meaning |
|---|---|---|
bad_ | 400 | The body is not JSON, the prompt is empty, or an image is not a base64 data:image/ URL. |
bad_ | 401 | Unknown or disabled key. |
domain_ | 403 | The key is locked to another domain, or the request has no Origin or Referer. Only Pro keys can be locked; see Domain lock. |
too_ | 413 | More than 32,000 characters of text, more than 50 input parts or 100 history messages, more than one image, or an image over 2 MB. |
quota_ | 429 | The key has used its tokens: all 1,000,000 of a free account, or this month's 100,000,000 on Pro. The message carries the upgrade link. |
rate_ | 429 | More than 20 requests a minute from one IP, or 60 a minute on one key. |
upstream | 502 | The AI provider returned an error. Try again. |
paused | 503 | OneTag AI reached its daily budget and is paused until 00:00 UTC. |
no_ | none | SDK only: no data-key on the script tag and no onetag.configure({ key }). |
bad_ | none | SDK only: an input it cannot read, such as a cross-origin image served without CORS. |
bad_ | none | SDK only: you passed json: true and the answer was not valid JSON. |
network | none | SDK only: the request failed, or a stream was cut off before it finished. |
aborted | none | SDK only: the signal you passed fired before the answer was complete. |
Limits
| Limit | Value |
|---|---|
| Tokens, Free | 1,000,000 in total per account (one key per email), input and output together; never reset |
| Tokens, Pro | 100,000,000 per calendar month (UTC), reset on the 1st |
| Text per call | 32,000 characters: prompt, system, text input and history together (the SDK trims long input to fit) |
| Input parts per call | 50 |
| History messages per call | 100 |
| Images per call | 1, up to 2 MB (the SDK downscales it to a 1,600 px JPEG before sending) |
| Answer length | 500 tokens by default, up to 1,000 with maxTokens |
| Requests per IP | 20 per minute |
| Requests per key | 60 per minute |
We email you when a key has used 80% and 100% of its tokens. A free key that is used up answers quota_exceeded until it is upgraded; a Pro key until the 1st.
Pricing
| Free | Pro | |
|---|---|---|
| Price | $0 | $9.9 per month, through Stripe |
| Tokens | 1,000,000 in total | 100,000,000 a month |
| Where the key works | Any page, any domain, curl | The same, or locked to your domain |
| Dashboard | Key and usage | Key, usage, domain lock, billing portal |
Upgrade with the email your key belongs to; the key itself does not change. Cancelling in the billing portal puts the key back on Free at the end of the paid month.
Domain lock (Pro)
A Pro key can be locked to a domain from its dashboard. Then it only works from browser pages on that domain or its subdomains, and on localhost or 127.0.0.1 while you build. A key locked to example.com works on www.example.com and blog.example.com, not on example.org. OneTag AI checks the Origin header the browser sends, or the Referer when there is no Origin.
A locked key rejects every request without an Origin or Referer with domain_not_allowed: curl, code running on a server, and pages opened from file://. A locked key is for browser pages on your site. Clear the domain on the manage page to allow everything again.
Free keys are never locked: they work from any page, any domain and curl, and anyone who copies the key out of your page source shares its 1,000,000 tokens. That is what the lock is for.
HTTP API
The SDK wraps one endpoint. You can call it directly from any language with a key that is not locked to a domain.
POST https://onetagai.com/v1/ask
Content-Type: application/json
| Field | Type | Notes |
|---|---|---|
key | string | Required. |
prompt | string | Required, not empty. |
input | string or array | A string, or a list of parts: {"type": "text", "text": "..."} and at most one {"type": "image", "data_url": "data:image/jpeg;base64,..."}. |
system | string | Optional extra instructions. |
json | boolean | Default false. When true, text holds a JSON object as a string. |
max_tokens | integer | Default 500, max 1000. |
stream | boolean | Default false. When true, the answer comes as server-sent events. |
history | array | Earlier turns: [{"role": "user" | "assistant", "content": "..."}]. |
Example
curl https://onetagai.com/v1/ask \
-H "Content-Type: application/json" \
-d '{"key": "YOUR_ONETAG_KEY", "prompt": "Summarize in one sentence", "input": "OneTag AI lets any static page call an AI model with one script tag, without a backend and without an API key in the page."}'
The answer, with the tokens it used:
{"text": "OneTag AI adds AI to static pages with a single script tag.", "usage": {"input": 61, "output": 14}}
Streaming
With "stream": true the response is text/event-stream: one data: line per event, with a blank line between events. The text arrives in delta events, and the last event carries done and the usage.
data: {"delta": "OneTag AI adds"}
data: {"delta": " AI to static pages"}
data: {"delta": " with a single script tag."}
data: {"done": true, "usage": {"input": 61, "output": 14}}
If the AI provider fails partway through, the stream ends with an error event instead: data: {"error": {"code": "upstream", "message": "..."}}.
Errors
Errors found before the answer starts come back as JSON with the HTTP status from the error table:
HTTP/1.1 401 Unauthorized
{"error": {"code": "bad_key", "message": "Unknown or disabled key."}}