Before you start
You need an API key. Open Settings, then Loopster API, and create one. See Create and revoke a Loopster API key. The role menu offers Member — read only, Editor — read, write and run and Admin — everything an API key can do. Give a key the least access the job needs.
The base URL is
https://app.loopster.ai/api. Every path starts with/v1, for examplehttps://app.loopster.ai/api/v1/tables.Send your key in the
Authorizationheader:Authorization: Bearer loopster_sk_…Your key decides which workspace you work in. No call takes a workspace id.
Table ids and row ids are UUIDs. Column ids start with
col_.Answers are JSON. A good answer has
success: true. A failed one hassuccess: falseand anerrormessage. Some also carry acodeyou can check.Most calls in this article also exist as tools on Loopster's MCP server. The tool names are at the end of the sections they belong to.
The full list of calls is the OpenAPI file at
https://app.loopster.ai/openapi.json. The base URL is also on the API Introduction page of the docs.
Which calls can spend from my wallet?
Your wallet pays for paid work. These calls can start paid work, so they can spend from it:
Creating a table from a paid starter.
POST /v1/tables. A blank table, a webhook table and asearchtable are free. Every other type runs a source while it creates the table.Inserting rows into a table that has Auto-run on.
POST /v1/tables/{tableId}/rows. The API calls Auto-runautoplay.Updating a row or cells.
PATCH /v1/tables/{tableId}/rows/{rowId}andPATCH /v1/tables/{tableId}/cells. Theupdate_rowandupdate_cellstool descriptions say that on a table with auto-rerun columns, writing a cell also re-queues the columns that read it, and that spends from your wallet.Triggering rows.
POST /v1/tables/{tableId}/trigger. WithoutrowIdsit runs every row in the table. Add?dryRun=trueto price it without running it.Running a function or a capability, and schedules. The MCP tools
run_function,run_capability(unless it runs on your own connection) andcreate_schedulesay they spend from your wallet. This article does not cover them.
These calls do not start paid work by themselves:
Creating a blank table, a webhook table or a
searchtable.Inserting rows into a table that has Auto-run off. The rows are only written.
Renaming a table, and switching
autoDedupeorcreateColumnsFromKeys.A trigger dry run.
Importing a file, exporting a table, restoring from Trash and creating a share link.
Paid work holds its worst-case cost from your wallet before it runs. You are charged what it really cost, never more. You can read your wallet with GET /v1/wallet and what you spent with GET /v1/usage. See Your wallet and keeping spend under control.
How do I list and read my tables?
GET /v1/tableslists the tables in your workspace, newest first.Filter with
search(part of the name, any case) orfolderId.Add
pageSize(1 to 500) and pass the returnednextCursorback ascursorto get the next page. WithoutpageSize, every matching table comes back in one answer.GET /v1/tables/{tableId}returns one table with every column's full configuration, its settings (includingautoplay,createColumnsFromKeys,apiEnabledandautoDedupe) and itsfolderId.GET /v1/tables/{tableId}/columnsreturns the same column list on its own. Use it to find column ids.
MCP tools: list_tables, get_table, list_columns.
How do I create a table?
Send
POST /v1/tables. Thetypefield defaults toblank.Read
table.autoplayin the answer. It is the Auto-run setting the table was created with.
curl -X POST https://app.loopster.ai/api/v1/tables \ -H "Authorization: Bearer loopster_sk_…" \ -H "Content-Type: application/json" \ -d '{"type": "blank", "autoplay": false}'
The types are blank, google-search, google-maps, instagram-search, linkedin-reactions, meta-ads, apify-scrape, luma-event, hubspot-import, salesforce-import, heyreach-import, webhook and search. A type that does not exist gets a 400 that names the valid ones. If the search a table runs matches nothing, the call answers 422 and no table is created.
What each type costs:
blankandwebhookare free. They create an empty table.searchimports a saved search result, by itssearchId, into a new table. It is free.Every other type runs its source while the table is created, and that can spend from your wallet. It holds its worst-case cost first and is charged what it really cost. If your wallet cannot cover it, the call is refused with 402
insufficient_balancebefore the table is created.
autoplay defaults to true. A blank table takes autoplay when you create it. For a paid starter type, create the table first, then change the setting with PATCH /v1/tables/{tableId}. In the app, a new table can start with Auto-run on or off, so check it on every table.
MCP tools: create_table, set_table_autoplay.
How do I rename a table or change its settings?
PATCH /v1/tables/{tableId} changes a table's title (1 to 256 characters) and these settings: autoplay, createColumnsFromKeys and autoDedupe.
A key the call does not read comes back in
ignoredKeysand changes nothing. A body with no readable keys is refused with 400.It never moves a table. Use the workbook calls in the next section.
apiEnabledis read-only here. Sendingfalseis refused with 400, and sendingtruechanges nothing. A table whoseapiEnabledis off answers 403.autoDedupetakesenabled,columnIdandkeep. See Update Table in the docs, and Table settings: Auto-run, scheduled reruns and deduplication for the app.
MCP tools: rename_table, set_table_autoplay, set_table_auto_dedupe, set_table_create_columns_from_keys.
How do I organize tables in workbooks and folders?
A folder holds workbooks. A workbook holds tables. Every table is in exactly one workbook, and a table's folder is its workbook's folder.
GET /v1/workbookslists your workbooks. Each has itsfolderId(null means the top level) and itstableIdsin tab order.GET /v1/folderslists your folders by name.POST /v1/folderscreates an empty folder.nameis required, 1 to 256 characters. It needs an editor key.POST /v1/workbookscreates an empty workbook at the top level or in a folder.nameis optional (1 to 256 characters, "Untitled Workbook" if you leave it out).folderIdis optional, and afolderIdthat is not one of your folders answers 404. It needs an editor key.PATCH /v1/workbooks/{workbookId}with{"folderId": "{folderId}"}moves a workbook and every table in it into a folder. AfolderIdofnullputs it at the top level. It needs an editor key.POST /v1/workbooks/{workbookId}/tableswith{"tableId": "{tableId}"}files a table in that workbook. The table takes the workbook's folder. A table'sfolderIdcannot be set on its own.Another workspace's table, workbook or folder answers 404.
In the app, click Table actions on the table's tab, then Move table.
MCP tools: list_workbooks, create_workbook, move_workbook, move_table, list_folders, create_folder.
Docs: List Workbooks, Move Workbook, List Folders.
How do I add rows to a table?
Find the table's id with
GET /v1/tables.Find its column ids and headers with
GET /v1/tables/{tableId}.Check whether the table's Auto-run is on. See the next section before you go on.
Send
POST /v1/tables/{tableId}/rowswith one row as a JSON object, or many rows as an array of objects. Each key is a column id or a column header.Read the answer. It is a 201 with
insertedCountand the new row ids inrows. Add?returnIds=falseto leave the ids out. The counts stay.
curl -X POST https://app.loopster.ai/api/v1/tables/{tableId}/rows \ -H "Authorization: Bearer loopster_sk_…" \ -H "Content-Type: application/json" \ -d '[{"{columnHeader}": "{value}"}, {"{columnHeader}": "{value}"}]'
What the call accepts:
Send the array itself. An object whose only key holds an array of rows, such as
{"records": [...]}, is refused.Every element of the array must be an object. A number, a string, null or a nested array is refused with 400, and the error names the index.
An empty object
{}inserts a blank row.A record none of whose keys matches a column is refused with 400, unless the table creates columns from keys or keeps the raw record in a trigger column.
Keys that match no column come back in
ignoredKeys. Those keys are ignored.Turn on
createColumnsFromKeys(a setting you change withPATCH /v1/tables/{tableId}) and each new key becomes a manual column, listed incolumnsCreated. It is off by default.A value over its column's size limit is stored empty and listed in
cappedCells. The rest of the write still works. The limits are 8 kB for typed text, 100 kB for references and 200 kB for enrichment output. Add?onOversize=rejectto refuse instead: nothing in the request is written, and the answer is 422.If auto-dedupe is on, the answer lists the rows it skipped as duplicates in
skipped, with the total inskippedCount.
One request carries at most 25,000 rows. A larger one is refused with 413 and code: too_many_rows before anything is written. The answer gives maxRows and received. The API description says to send larger loads in batches of 10,000 rows or fewer, or to import a file. An insert answers only after it finishes, within 300 seconds, and one still running at 300 seconds is cut off.
MCP tool: insert_rows (it has the same 25,000-row limit).
What does Auto-run do when I insert rows?
The API calls the setting autoplay. The app calls it Auto-run. It decides whether rows you insert run the table's enrichment columns by themselves.
On: rows you insert are queued for processing, and that can spend from your wallet.
Off: an insert only writes rows. Nothing runs or spends until you trigger rows.
Switching Auto-run on does not run the rows already in the table. Use
POST /v1/tables/{tableId}/triggerfor those.
In the app, Auto-run is at the top left of the table toolbar. When it is Off, the app says: "New rows and values will not run on their own — trigger them from a column or row. Cells already queued still run." See Run columns safely: Auto-run, test one row, then run the rest.
How do I check whether an insert started paid work?
Before you insert, read the table with
GET /v1/tables/{tableId}. Ifautoplayistrue, rows you insert are queued for processing.After you insert, read
enrichmentin the answer. The answer also hasstartedEnrichment(true or false), but readenrichmentinstead. Its values are:started: the run was handed over and at least one column executes. Work is running, and it can spend from your wallet.none: nothing was meant to run.not_started: Loopster could not hand the run to its workers. The answer includesrowsToTrigger. Trigger those row ids and do not insert again.unknown: the run was sent but not confirmed, so the rows may or may not be running. Check the table's status before you trigger, so the rows are not run twice.
Follow progress with
GET /v1/tables/{tableId}/status. Itsrowsblock counts total, idle, queued, generating, complete and error. Enrichment is asynchronous and can take minutes.If cells show errors, status tells you how many, but not why. Read the row to see the message. See What a cell's message means.
To see what was spent, read
GET /v1/usage.
The rows are stored in every case, whatever enrichment says. startedEnrichment is true only when Auto-run is on, rows were inserted, and the table has at least one column that an automatic run executes, such as an AI column with its prompt. A formula column does not count.
MCP tools: get_table, get_status, trigger_rows, get_usage.
How do I stop work that an insert started?
Send POST /v1/tables/{tableId}/stop. It stops all ongoing processing for the table.
It does not refund work that was already charged.
The answer reports
queuedJobsRemoved, which is queued work removed before it started, so nothing was charged for it.It also reports
stillRunning: how many cells may still run and bill, because their work was already past the point where it could be stopped. A null count means the worker did not report it. That is not the same as zero.It is safe to call twice. With nothing running it reports zero.
Then set autoplay to false with PATCH /v1/tables/{tableId} if you do not want later inserts to run.
MCP tool: stop_run.
How do I read rows and get their ids?
Row ids come back in
rowswhen you insert. You can also read them withGET /v1/tables/{tableId}/rows.GET /v1/tables/{tableId}/rowsreturns rows a page at a time.pagestarts at 1.pageSizeis 50 by default and 500 at most. Out-of-range numbers are clamped, and a value that is not a number is a 400.To read a saved view, send its
viewId. That read pages by cursor: passpagination.nextCursorback ascursor. Do not sendpagewithviewId. Reading withfilter,sortorupdatedSincepages by cursor too.columnslimits which cells come back.Each row has
cellskeyed by column id,cellStatuses, andcellErrorsfor cells that failed. Itsstatusis worked out from its cells. It shows work in progress if any cell is still working. Otherwise it iserrorif any cell failed,completeonce anything has run, oridle.GET /v1/tables/{tableId}/rows/{rowId}reads one row.
MCP tools: list_rows, get_row.
How do I update a row or cells?
To change one row, send PATCH /v1/tables/{tableId}/rows/{rowId}. The keys are column ids or column headers.
To change many cells across rows in one request, send PATCH /v1/tables/{tableId}/cells:
curl -X PATCH https://app.loopster.ai/api/v1/tables/{tableId}/cells \ -H "Authorization: Bearer loopster_sk_…" \ -H "Content-Type: application/json" \ -d '{"cells": [{"rowId": "{rowId}", "columnId": "{columnId}", "value": "{value}"}]}'
Each entry needs
rowId(a UUID),columnId(an existing column) andvalue. Sendnullto clear a cell. Leavingvalueout is a 400.Each
rowIdandcolumnIdpair may appear only once per request.modeissetby default.appendadds the value after what the cell holds, with an optionalseparatorof up to 16 characters. An append is written only if the cell has not changed since it was read. It is retried up to five times, then comes back inrejectedasstale.A cell that a run is still computing is not written. On
PATCH /v1/tables/{tableId}/rows/{rowId}, if that leaves nothing written, the answer is 409 withcode: cell_running. If other cells were written, the answer is 200 and the held cells are inrejectedwithreason: running. Send those again once the run finishes.Update Cells takes
columnId, not a header. The MCP tools accept a column's name or its id.Cells that were not written come back in
rejectedwith a reason:not_in_table,deduped,stale,value_too_largeorrunning.Oversized values work the same way as on insert:
cappedCellsby default, or 422 with?onOversize=reject.On a table with auto-rerun columns, an update can spend from your wallet. See the section on which calls can spend from your wallet, near the top.
MCP tools: update_row, update_cells.
How do I delete rows?
Read the ids of the rows to delete. See the section on reading rows.
Send
DELETE /v1/tables/{tableId}/rowswith{"rowIds": ["{rowId}", "{rowId}"]}. Send at least one id. Each id must be a UUID, and a request with any id that is not one is a 400.Read the answer.
deletedCountsays how many rows went. Ids that are not rows of the table are listed inskippedwith the reasonnot_in_table.
Deleted rows do not go to Trash. Export the table first if you may need them back.
MCP tool: delete_rows. It has no "delete every row" form, on purpose.
How do I delete a table and get it back?
DELETE /v1/tables/{tableId} moves the table to Trash. It is the same delete the app does. The table stops appearing in listings. Its rows and cells are kept. You can restore it for 30 days, then it is permanently purged. Deleting a table that is already in Trash answers 404.
Deleting a table does not stop work that is already running. Stop the run first, as in the section above. Otherwise the queued work finishes and bills against the table in Trash.
To get a table back:
Send
GET /v1/trash. It lists what Trash holds: tables deleted on their own, workbooks and functions, newest first. Each item haspurgesAt, the moment it stops being recoverable. A workbook showstableCount. A table that went down with its workbook is not listed on its own.Send
POST /v1/trash/{itemId}/restorewith the id from that list. You do not send its kind. It needs an editor key.
A workbook comes back with its tables. A table restored on its own is placed in a workbook. Nothing runs and nothing is billed by restoring.
In the app, click the bin icon at the bottom of the left sidebar, then Restore on the item's row. See Restore deleted tables and functions from the Trash. Docs: Restore from Trash.
MCP tools: delete_table, list_trash, restore_from_trash, stop_run.
How do I import a file?
Importing takes three steps: get an upload address, upload the file, then start the import. After that you check the job. For more than about 10,000 rows, import a file instead of inserting rows.
Send
POST /v1/tables/{tableId}/imports/upload-url. Sendformatascsv(the default) orndjson, which is one JSON object per line whose keys are its columns. The answer hasfileId,uploadUrlandexpiresAt. It needs an editor key.PUTthe file's bytes touploadUrlbeforeexpiresAt, which is two hours away. The address works once. Uploading changes nothing in the table.Send
POST /v1/tables/{tableId}/importswith thefileId. It answers 202 at once with ajobIdand the statusqueued. It needs an editor key.Send
GET /v1/jobs/{jobId}untilstatusiscompleteorfailed. It moves throughqueuedandrunning. While it runs,processedandtotalcount the rows read from the file. When it completes,insertedis how many rows the table gained.
Choosing which file column goes to which table column:
Without
mapping, each key in the file goes to the column with that name or id. A key that matches no column gets a new column, or is skipped if you sendcreateColumnsFromKeys: false. A column name that two columns share is never guessed between.With
mapping(up to 500 entries), only the keys it lists are imported.{key, column}sends a key to a column, by id or by name.{key, action: "create"}makes a new column.{key, action: "skip"}leaves the key out.For a CSV file,
hasHeaderRow: falsemeans the first line is data. The columns are then calledColumn 0,Column 1and so on.
What to know about the limits:
A file can be up to 50 MB. A larger file fails the job, and the job's
errornames the size.A
fileIdimports once. The file is deleted when its import ends, whether it completes or fails. To import again, upload it again.A file that is never imported is deleted after seven days. A job is kept for a week.
An import starts no enrichment and costs nothing. That is different from inserting rows into a table with Auto-run on. The table's row limit and auto-dedupe apply, as they do in the app.
In the app, Home, then Import data, then Import CSV / XLSX takes .csv and .xlsx files. The API calls above take CSV and JSON lines. See Import a CSV or Excel file.
MCP tools: create_upload_link, import_file, get_job.
Docs: Get an Upload Address, Import a File, Get a Job.
How do I export a table?
Send
POST /v1/tables/{tableId}/exports. Sendformatascsv(the default) orjsonl. Send aviewIdto export what that view shows: its filters, sort and visible columns. Leave it out to export the whole table. It needs member access and costs nothing.The call answers 202 at once with an
exportIdand the statusqueued.Send
GET /v1/exports/{exportId}untilstatusiscomplete. It moves throughqueuedandrunning, or ends asfailedwith anerrorthat says why.When it is complete, the answer gives
rowCountandresultUrl, the download link. Both are null until then.
In jsonl, each row is one JSON object keyed by column header, with the row's _row_id.
The download link is signed on every request, so each poll returns a fresh one. It works until expiresAt, which is an hour from that request, or until the file itself is deleted, seven days after it was made, whichever comes first. An export from another workspace, or one whose file is deleted, is not found.
In the app, click Add column, then Add enrichment, open the Export tab, and use Download CSV or Download Excel under Download. See Get data out: download a file or send rows to Slack.
MCP tools: export_table, get_export.
Docs: Get an Export.
How do I share a table with a link?
Send
POST /v1/tables/{tableId}/shares. It creates a public, read-only link to the whole table. It needs an editor key.To make the link end by itself, send
expiresInSeconds, from 60 to 31,536,000 (a year). Leave it out for a link that works until you revoke it.
curl -X POST https://app.loopster.ai/api/v1/tables/{tableId}/shares \ -H "Authorization: Bearer loopster_sk_…" \ -H "Content-Type: application/json" \ -d '{"expiresInSeconds": {seconds}}'
Anyone who has the URL can open it without signing in and read every row and column until you revoke the link or it expires. Share it only when you mean to.
The link belongs to the workspace. It stays revocable after the person who set up the key has left.
A link shows each column's settings without the webhook addresses, headers and tokens saved in them. Text typed into a request body travels with the link, so put credentials in a header or token field.
GET /v1/tables/{tableId}/shareslists the live links, newest first, whoever made them.mintedByisapi_keyoruser, and links made in the app are included. Revoked and expired links are not listed. It needs an editor key.DELETE /v1/tables/{tableId}/shares/{shareId}stops a link at once. Use the link'sshareId. It cannot be turned back on. A link that is not live answers 404. It needs an editor key.
In the app, open More table options and choose Share. The panel is called "Share table via URL or Template". The button is Share table URL.
MCP tools: create_share_link, list_share_links, revoke_share_link.
How do I retry a call without doing the work twice?
Send an Idempotency-Key header. It makes a retry safe. These calls take it: POST /v1/tables, POST /v1/tables/{tableId}/rows, PATCH /v1/tables/{tableId}/rows/{rowId}, PATCH /v1/tables/{tableId}/cells and POST /v1/tables/{tableId}/trigger. Three calls this article does not cover take it too: POST /v1/webhooks/{secret}, POST /v1/capabilities/{templateId}/runs and POST /v1/functions/{functionId}/runs.
You choose the key. It is 1 to 128 characters: letters, digits,
_,-,.and:. A fresh UUID works.Use a new key for each new action. Use the same key when you retry that action.
If you repeat the same request with the same key within 24 hours, you get the first answer back. It has
"replayed": trueand anIdempotent-Replayed: trueheader. Nothing runs or bills again.A failure that was stored replays as the same failure. Send a new key to try again.
Appending to a cell is not idempotent. A retried append adds the text twice, so send a key whenever you may retry.
In MCP the argument is idempotencyKey, on create_table, insert_rows, update_row, update_cells, trigger_rows, run_capability and run_function.
Troubleshooting
My insert started work I did not want
What you see: enrichment is started in the answer to an insert, and you did not want the table to run.
Why it happens: The table's Auto-run (autoplay) was on, and the table has at least one column that an automatic run executes.
What to do: Send POST /v1/tables/{tableId}/stop. Then set autoplay to false with PATCH /v1/tables/{tableId}. Stopping does not refund work already charged, and stillRunning shows cells that may still run and bill. Next time, check autoplay before you insert.
My insert says not_started or unknown
What you see: enrichment is not_started or unknown.
Why it happens: For not_started, Loopster could not hand the run to its workers. For unknown, the run was sent but not confirmed. The rows are stored in both cases.
What to do: For not_started, trigger the ids in rowsToTrigger and do not insert again. For unknown, read GET /v1/tables/{tableId}/status first, so you do not run the rows twice.
My insert was refused with 413
What you see: A 413 with code: too_many_rows, plus maxRows and received.
Why it happens: One request carries at most 25,000 rows. Nothing was written.
What to do: The API description advises batches of 10,000 rows or fewer. You can also import the file.
My insert was refused with 400
What you see: A 400 from POST /v1/tables/{tableId}/rows.
Why it happens: A record would be stored empty because none of its keys match a column. Or an element of the array is not an object, and the error names its index. Or you sent {"records": [...]} instead of the array.
What to do: Send the array itself. Use real column ids or headers as keys, or turn on createColumnsFromKeys. A table id or row id that is not a UUID also gets a 400, not a 404, so a typo can be told apart from a deleted table.
Some of my data did not land in the table
What you see: Keys in ignoredKeys, rows in skipped, a cell listed in cappedCells, or an import job whose inserted is lower than the file's row count.
Why it happens: ignoredKeys are keys that match no column. skipped rows were treated as duplicates by auto-dedupe. cappedCells are values over their column's size limit, stored empty. A lower inserted count means auto-dedupe skipped rows, or the table's row limit cut the import short and limitedByRowLimit is true.
What to do: Use a column's id or its header as the key, or turn on createColumnsFromKeys so each new key becomes a manual column. Shorten long values, or add ?onOversize=reject to refuse them with 422. Check the table's autoDedupe setting and the job's limitedByRowLimit.
My update answered 409 cell_running
What you see: A 409 with code: cell_running from PATCH /v1/tables/{tableId}/rows/{rowId}. Or a 200 with held cells in rejected and reason: running.
Why it happens: A run is still computing those cells, and a cell that a run is computing is not written.
What to do: Wait for the run to finish, then send the same request again. After a 409 on a row update the Idempotency-Key is not spent, so you can reuse the same key.
My import failed
What you see: The job's status is failed.
Why it happens: The job's error gives the reason when it is yours to fix, for example a line that is not JSON, a column that no longer exists, or a file over 50 MB. Otherwise it says "The import failed."
What to do: Fix what the error names. Then get a new upload address and upload the file again, because a fileId imports only once.
My export link does not work
What you see: The resultUrl stops working, or the export is not found.
Why it happens: The link works until expiresAt, an hour from the request that returned it. The file is deleted seven days after it was made.
What to do: Send GET /v1/exports/{exportId} again for a fresh link. After seven days, start a new export.
A restore answered 404
What you see: POST /v1/trash/{itemId}/restore answers 404.
Why it happens: The id is not in your workspace's Trash. It may already be restored, past its purge date, or a table that went down with its workbook.
What to do: Send GET /v1/trash and use an id from the list. For a table inside a deleted workbook, restore the workbook.
A call answered 402
What you see: A 402 with a code of insufficient_balance, workspace_limit or daily_cogs_limit, plus requiredUsd and spendableUsd.
Why it happens: The call would start paid work and a limit refused it. Nothing was created, queued or charged. insufficient_balance means the wallet cannot cover it. workspace_limit means the workspace's own spend limit leaves less than the wallet. daily_cogs_limit means the account reached its daily spend safety limit.
What to do: For insufficient_balance, click the wallet button, then Manage wallet, then Top up, or ask for less. See Top up your wallet, set auto top-up and read your funds history. For workspace_limit, open Settings, then Workspaces, click the workspace and change its Spend limit with Set spend limit. See Workspaces: create one and set a spend limit. Topping up does not lift daily_cogs_limit. It resets at midnight UTC, and Loopster support can raise it. See Contact support.
A call answered 403
What you see: A 403. If your key's role is too low, the body also has requiredRole and role.
Why it happens: The call is not allowed for this key. Reasons include a role below what the call needs, a resource that belongs to another workspace, and a table whose apiEnabled setting is off.
What to do: If requiredRole is named, use a key with that role or higher. Create one under Settings, then Loopster API. Check that the table or item is in the same workspace as your key. You cannot switch apiEnabled on through the API. If that is the cause, tell us through Need Help?.
My calls suddenly answer 401 key_expired
What you see: A 401 with code: key_expired. The expiry date is in error.
Why it happens: A key stops working once it is past its expiry. You choose the expiry when you create the key, and it cannot be changed afterwards.
What to do: Create a new key under Settings, then Loopster API. Call GET /v1/key with a working key to see that key's expiresAt. It is null for a key that never expires.
A call answered 429
What you see: A 429 with a Retry-After header.
Why it happens: A rate limit was reached, and nothing was done.
What to do: Wait the number of seconds in Retry-After, then send the call again.
Good to know
The API's
autoplayis the app's Auto-run. Check it on every table before you insert rows.Auto-dedupe deletes rows. An edit that makes a row match another can delete a row. Check the column before you turn it on.
Deleted tables go to Trash for 30 days. Deleted rows do not go to Trash.
A key belongs to one workspace. Create and revoke keys under Settings, then Loopster API.
The API docs are at docs.loopster.ai. In the app, API Docs is at the bottom of the Settings left menu.
If a refusal looks wrong, click Need Help? at the top right of any page and say what call you sent and what you saw. Do not paste your key.
More about the API and MCP


