autario.js: the data SDK for apps
Any HTML page can become an app on autario, including one that Claude, v0, Lovable or ChatGPT wrote for you a minute ago. The page asks autario.js for data, autario answers as the person looking at the page, and the page never holds a key, a token or a network connection of its own. This page is the whole reference.
Use it in one line
Add the script tag, or leave it out: autario inlines autario.js into every app it serves, so window.autario always exists inside an app on autario. Outside autario the calls reject with the reason not_in_autario, because there is nobody to answer them.
<script src="https://autario.com/autario.js"></script>
<script>
(async () => {
const { app, user } = await autario.ready();
const [first] = await autario.datasets();
const rows = await autario.query(first.id, { limit: 10 });
console.log(app.name, user.role, rows);
})();
</script>
The fastest way to try it is the Build page: paste the HTML, pick the datasets the app may read, press Run it, and the app opens inside autario on your own data. Publish puts it into My apps.
The calls
- autario.ready() resolves with { app: { id, name }, user: { id, email, role } }. role is owner, admin, editor, reader or visitor. It is cached, so call it wherever you need it.
- autario.datasets() resolves with the datasets this app may read for this viewer: the owner's datasets the app declares, each with id, title, kind, connector, rows, updated_at and columns. A visitor gets an empty list.
- autario.query(datasetId, { columns, where, orderBy, limit, offset }) resolves with an array of row objects. columns is a list of names, where is { column: value } or a list of [column, op, value] with op one of eq, neq, gt, lt, gte, lte, like, orderBy is 'column' or 'column desc', limit defaults to 100. The array carries rows.meta with total, returned, limit and offset.
- autario.csvUrl(datasetId) returns the download URL of the whole dataset as CSV, for Excel, Power BI or curl with your API key. The app itself cannot download it, because it has no network.
- autario.artifacts.get(key) and autario.artifacts.set(key, value) keep the app's saved state, one JSON value of at most 256 KB per key. get needs the reader role, set needs editor or higher.
- autario.publicDatasets.search(q) and autario.publicDatasets.query(id, options) read the open catalogue, World Bank, FRED, Eurostat, OECD and the rest, with no sign-in and no declaration.
The same row limits, filters and sort apply as on GET /api/v1/datasets/<id>/query, because both run the same query builder.
Who sees what
Every call runs as the person looking at the app, never as the app. The app's owner decides which of his datasets the app may read, and his team decides who may look.
- The owner reads the datasets the app declares. The declaration is the dataset picker on the Build page, or the datasets list on create_app, or consumed_datasets entries of the form { "op": "datasets-data", "dataset": "<id>" }.
- A team member invited on Account > Team with a role on the app reads the same declared datasets of the owner. reader reads, editor also saves artifacts, admin also manages the app's people.
- Anyone else is a visitor: public catalogue data only, even on a public app.
- An undeclared private dataset answers not_found for a member and dataset_not_declared for the owner, with the fix in the message.
Errors
Every call rejects with a plain object { error, reason, status }. error is a sentence for a person, reason is the word to branch on.
- not_found: the dataset does not exist or this viewer may not read it.
- dataset_not_declared: the owner asked for his own dataset the app does not declare.
- role_required: saving needs editor or higher, reading saved state needs reader.
- rate_limited: more than 300 calls a minute for this viewer. Wait and retry.
- bad_query: a column name in columns, where or orderBy does not exist.
- not_in_autario and timeout: the page is not running inside autario, or autario did not answer.
Why the app never holds a key
An app runs in a sandboxed frame with a null origin and no network: it cannot fetch, open a socket or read autario cookies. autario.js posts a question to the autario page around it, that page asks autario with the viewer's own signed-in session, and only the answer travels back into the app. Sign out and every call answers as a visitor. That is why you can open a stranger's app on your own data without auditing its code.
A complete example
Forty lines: pick one of the app's datasets and show its top ten rows. Paste it on the Build page, or ask Claude to start from it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Top rows</title>
<script src="https://autario.com/autario.js"></script>
<style>
body { font: 14px system-ui, sans-serif; margin: 20px; color: #111827; }
select { font: inherit; padding: 6px 8px; }
table { border-collapse: collapse; margin-top: 14px; width: 100%; }
th, td { border-bottom: 1px solid #e5e7eb; padding: 6px 8px; text-align: left; }
th { background: #f9fafb; }
.muted { color: #6b7280; }
</style>
</head>
<body>
<h1 id="title">Top rows</h1>
<p class="muted" id="who">Loading...</p>
<select id="pick" aria-label="Dataset"></select>
<table id="rows"></table>
<script>
const esc = (v) => String(v == null ? '' : v).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
async function show(id) {
const rows = await autario.query(id, { limit: 10 });
const cols = rows.length ? Object.keys(rows[0]).filter((c) => !c.startsWith('autario_') && c !== 'dd_id') : [];
document.getElementById('rows').innerHTML =
'<tr>' + cols.map((c) => '<th>' + esc(c) + '</th>').join('') + '</tr>' +
rows.map((r) => '<tr>' + cols.map((c) => '<td>' + esc(r[c]) + '</td>').join('') + '</tr>').join('');
}
(async () => {
const { app, user } = await autario.ready();
document.getElementById('title').textContent = app.name;
document.getElementById('who').textContent = 'Signed in as ' + (user.email || 'a visitor') + ' (' + user.role + ')';
const list = await autario.datasets();
if (!list.length) { document.getElementById('who').textContent += '. This app declares no dataset you can read.'; return; }
const pick = document.getElementById('pick');
pick.innerHTML = list.map((d) => '<option value="' + esc(d.id) + '">' + esc(d.title) + '</option>').join('');
pick.onchange = () => show(pick.value).catch((e) => { document.getElementById('who').textContent = e.error; });
await show(list[0].id);
})().catch((e) => { document.getElementById('who').textContent = e.error || String(e); });
</script>
</body>
</html>