Skip to main content

Create tables and add rows through the API

This article shows how to create tables, add and change rows, import and export files, and share a table through the Loopster API.

Written by Umer Ishaq

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 example https://app.loopster.ai/api/v1/tables.

  • Send your key in the Authorization header: 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 has success: false and an error message. Some also carry a code you 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 a search table 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-run autoplay.

  • Updating a row or cells. PATCH /v1/tables/{tableId}/rows/{rowId} and PATCH /v1/tables/{tableId}/cells. The update_row and update_cells tool 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. Without rowIds it runs every row in the table. Add ?dryRun=true to 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) and create_schedule say 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 search table.

  • Inserting rows into a table that has Auto-run off. The rows are only written.

  • Renaming a table, and switching autoDedupe or createColumnsFromKeys.

  • 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/tables lists the tables in your workspace, newest first.

  • Filter with search (part of the name, any case) or folderId.

  • Add pageSize (1 to 500) and pass the returned nextCursor back as cursor to get the next page. Without pageSize, every matching table comes back in one answer.

  • GET /v1/tables/{tableId} returns one table with every column's full configuration, its settings (including autoplay, createColumnsFromKeys, apiEnabled and autoDedupe) and its folderId.

  • GET /v1/tables/{tableId}/columns returns 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?

  1. Send POST /v1/tables. The type field defaults to blank.

  2. Read table.autoplay in 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:

  • blank and webhook are free. They create an empty table.

  • search imports a saved search result, by its searchId, 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_balance before 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 ignoredKeys and 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.

  • apiEnabled is read-only here. Sending false is refused with 400, and sending true changes nothing. A table whose apiEnabled is off answers 403.

  • autoDedupe takes enabled, columnId and keep. 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/workbooks lists your workbooks. Each has its folderId (null means the top level) and its tableIds in tab order.

  • GET /v1/folders lists your folders by name.

  • POST /v1/folders creates an empty folder. name is required, 1 to 256 characters. It needs an editor key.

  • POST /v1/workbooks creates an empty workbook at the top level or in a folder. name is optional (1 to 256 characters, "Untitled Workbook" if you leave it out). folderId is optional, and a folderId that 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. A folderId of null puts it at the top level. It needs an editor key.

  • POST /v1/workbooks/{workbookId}/tables with {"tableId": "{tableId}"} files a table in that workbook. The table takes the workbook's folder. A table's folderId cannot 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.

How do I add rows to a table?

  1. Find the table's id with GET /v1/tables.

  2. Find its column ids and headers with GET /v1/tables/{tableId}.

  3. Check whether the table's Auto-run is on. See the next section before you go on.

  4. Send POST /v1/tables/{tableId}/rows with one row as a JSON object, or many rows as an array of objects. Each key is a column id or a column header.

  5. Read the answer. It is a 201 with insertedCount and the new row ids in rows. Add ?returnIds=false to 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 with PATCH /v1/tables/{tableId}) and each new key becomes a manual column, listed in columnsCreated. 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=reject to 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 in skippedCount.

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}/trigger for 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?

  1. Before you insert, read the table with GET /v1/tables/{tableId}. If autoplay is true, rows you insert are queued for processing.

  2. After you insert, read enrichment in the answer. The answer also has startedEnrichment (true or false), but read enrichment instead. 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 includes rowsToTrigger. 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.

  3. Follow progress with GET /v1/tables/{tableId}/status. Its rows block counts total, idle, queued, generating, complete and error. Enrichment is asynchronous and can take minutes.

  4. 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.

  5. 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 rows when you insert. You can also read them with GET /v1/tables/{tableId}/rows.

  • GET /v1/tables/{tableId}/rows returns rows a page at a time. page starts at 1. pageSize is 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: pass pagination.nextCursor back as cursor. Do not send page with viewId. Reading with filter, sort or updatedSince pages by cursor too.

  • columns limits which cells come back.

  • Each row has cells keyed by column id, cellStatuses, and cellErrors for cells that failed. Its status is worked out from its cells. It shows work in progress if any cell is still working. Otherwise it is error if any cell failed, complete once anything has run, or idle.

  • 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) and value. Send null to clear a cell. Leaving value out is a 400.

  • Each rowId and columnId pair may appear only once per request.

  • mode is set by default. append adds the value after what the cell holds, with an optional separator of 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 in rejected as stale.

  • 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 with code: cell_running. If other cells were written, the answer is 200 and the held cells are in rejected with reason: 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 rejected with a reason: not_in_table, deduped, stale, value_too_large or running.

  • Oversized values work the same way as on insert: cappedCells by 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?

  1. Read the ids of the rows to delete. See the section on reading rows.

  2. Send DELETE /v1/tables/{tableId}/rows with {"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.

  3. Read the answer. deletedCount says how many rows went. Ids that are not rows of the table are listed in skipped with the reason not_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:

  1. Send GET /v1/trash. It lists what Trash holds: tables deleted on their own, workbooks and functions, newest first. Each item has purgesAt, the moment it stops being recoverable. A workbook shows tableCount. A table that went down with its workbook is not listed on its own.

  2. Send POST /v1/trash/{itemId}/restore with 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.

The Get an Upload Address page in the Loopster API docs. It says importing a file takes three steps, advises a file import over Insert Rows for more than about 10,000 rows, and shows the format field: csv, the default, or ndjson.

  1. Send POST /v1/tables/{tableId}/imports/upload-url. Send format as csv (the default) or ndjson, which is one JSON object per line whose keys are its columns. The answer has fileId, uploadUrl and expiresAt. It needs an editor key.

  2. PUT the file's bytes to uploadUrl before expiresAt, which is two hours away. The address works once. Uploading changes nothing in the table.

  3. Send POST /v1/tables/{tableId}/imports with the fileId. It answers 202 at once with a jobId and the status queued. It needs an editor key.

  4. Send GET /v1/jobs/{jobId} until status is complete or failed. It moves through queued and running. While it runs, processed and total count the rows read from the file. When it completes, inserted is 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 send createColumnsFromKeys: 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: false means the first line is data. The columns are then called Column 0, Column 1 and 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 error names the size.

  • A fileId imports 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.

How do I export a table?

  1. Send POST /v1/tables/{tableId}/exports. Send format as csv (the default) or jsonl. Send a viewId to 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.

  2. The call answers 202 at once with an exportId and the status queued.

  3. Send GET /v1/exports/{exportId} until status is complete. It moves through queued and running, or ends as failed with an error that says why.

  4. When it is complete, the answer gives rowCount and resultUrl, 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.

How do I share a table with a link?

  1. Send POST /v1/tables/{tableId}/shares. It creates a public, read-only link to the whole table. It needs an editor key.

  2. 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}}'

A warning on the Create Share Link page in the Loopster API docs: anyone holding the URL can read every row and column of the table until the link is revoked or expires.

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}/shares lists the live links, newest first, whoever made them. mintedBy is api_key or user, 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's shareId. 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": true and an Idempotent-Replayed: true header. 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 autoplay is 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

Did this answer your question?