Dataset to REST API (Sync Rows to Any API) avatar

Dataset to REST API (Sync Rows to Any API)

Pricing

from $3.50 / 1,000 requests

Go to Apify Store
Dataset to REST API (Sync Rows to Any API)

Dataset to REST API (Sync Rows to Any API)

Sends every row of an Apify dataset, file or Google Sheet URL, or JSON array to any REST API as its own request. Templated URL, body and headers with {{field}} placeholders, auth presets, rate limits, retries. One result row per request. Charged per answered request. Agent-ready via x402 and MCP.

Pricing

from $3.50 / 1,000 requests

Rating

0.0

(0)

Developer

Adam Pearce

Adam Pearce

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 hours ago

Last modified

Share

You have the rows. Your CRM, your app, your warehouse or your client's system wants them, one record at a time, over its own API. Right now that means writing a script: loop the rows, build the JSON, add the auth header, handle the rate limit, retry the 429s, and keep a note of which rows the API actually accepted.

This Actor is that script, as a form you fill in.

Point it at an Apify dataset, a CSV or Excel file, a Google Sheet or a JSON array. Write your endpoint once, with {{placeholders}} where the row's values go. Every row is sent as its own request, and you get one result row back per request telling you exactly what the API said.

What it does

  • Reads from anywhere: an Apify dataset (any scraper's output), a public CSV, TSV, Excel, JSON or JSON Lines file by URL, a Google Sheet link, or an inline JSON array.
  • Templates every part of the request: {{field}} works in the URL path, the query parameters, the headers and the body. Nested fields work too ({{address.city}}), and values going into the URL are encoded for you.
  • Keeps your data types: a body template value of exactly {{mrr}} sends the number 49, not the string "49". That matters to APIs that validate their input.
  • Handles auth for you: Bearer token, Basic, an API key in a header, or an API key in the query string. The secret is stored encrypted by Apify, never logged, and never echoed back into the output.
  • Respects the API: requests are spaced to your requests-per-second setting, retried with doubling backoff on 429, 408 and 5xx, and a Retry-After header from the API is obeyed. A 400 or 401 is a real answer, so it is never retried.
  • Tells you what happened, row by row: status, HTTP code, attempts, how long it took, the start of the response body, the parsed JSON, and any of your own row fields you asked to carry across so results join back to your data.
  • Dry run first: see the exact requests, resolved, before a single one is sent. Nothing is sent and nothing is charged.

Example

Input rows (from a dataset, a sheet, or pasted JSON):

idemailnamemrr
1001ana@example.comAna Silva49
1002ben@example.comBen Okafor19

Settings:

  • Endpoint URL: https://api.example.com/contacts/{{id}}
  • HTTP method: PUT
  • Request body: use a body template
  • Body template: {"fields": {"Email": "{{email}}", "Name": "{{name}}", "Value": "{{mrr}}"}}
  • Authentication: Bearer token, secret pasted in

Row 1001 goes out as PUT https://api.example.com/contacts/1001 with an Authorization: Bearer ... header and this body:

{ "fields": { "Email": "ana@example.com", "Name": "Ana Silva", "Value": 49 } }

And you get back:

rowIndexemailstatushttpStatusattemptsresponseSnippet
0ana@example.comsuccess2001{"id":"rec123","created":true}
1ben@example.comfailed4221{"error":"Value must be a number"}

One glance tells you which rows landed and which need fixing.

What it is good for

  • Push a scraper's results straight into your CRM, Airtable, Notion, HubSpot, Baserow or Supabase REST API on a schedule.
  • Fire one webhook per row into Zapier, Make, n8n or your own app.
  • Enrich rows through a third-party API and keep the response next to the row that produced it.
  • Bulk-create or bulk-update records in an internal tool that has an API but no import screen.
  • Give an AI agent one call that sends a whole dataset to an API, instead of a loop it has to write and pay for.

Pricing

Pay per event, no subscription.

EventPrice
Request$0.005 per request that got a reply
Webhook delivery$0.02, only when your endpoint confirms receipt
Actor start$0.00005 per run

Store discounts apply automatically: Bronze 10% off, Silver 20% off, Gold 30% off every event.

What that costs in practice. A nightly sync of 200 new rows is $1.00 a night, about $30 a month. A one-off import of 1,000 rows is $5.00. Sending 50 rows an hour, around the clock, is about $180 a month. If your API accepts arrays, raising Rows per request to 50 turns that same 1,000-row import into 20 requests, or 10 cents.

What is not charged. A dry run costs nothing but the run start. A request that never got a reply at all (a timeout, a DNS failure, a refused connection) costs nothing, because nothing was delivered. Retries of the same row are one request, not one per attempt. Rows skipped because an earlier one failed, or because a placeholder was empty, cost nothing. A failed webhook delivery costs nothing.

A request that came back 400, 401 or 500 is charged, because your API was reached and it answered. That answer is usually the most useful thing in the run.

Notes worth knowing before your first real run

  • Start with Dry run on. It shows the first three requests exactly as they would go out, including the resolved URL and body, with header names only so your token is never printed. When it looks right, turn it off.
  • Misspelt placeholders are caught up front. If no row has a field you used in a placeholder, the run warns you by name rather than quietly sending blanks.
  • Rows per request is the cost lever. At 1, each row is its own request, which is what most APIs want. If yours accepts an array of records, raising it packs that many rows into one charged request. Use the Batch wrapper field if the API expects {"records": [...]} rather than a bare array.
  • Requests per second defaults to 5. Lower it if the API pushes back. Retries and Retry-After are handled either way.
  • Order is preserved and results are joinable. Use Row fields to copy into the results to carry an id or an email onto its result row.

FAQ

What happens to rows the API rejects? Nothing stops, by default. Every row is attempted and the rejects come back as failed with the API's own status code and response body, so you can fix and re-run just those. Turn on Stop on first error if you would rather the run halt at the first problem, for example on a careful first run against a live system.

Can it send to an API that needs a signed or custom header? Yes. Anything you put in Headers (JSON) is sent as-is and supports {{placeholders}}, so a per-row token or signature that already exists in your data works. That field is stored encrypted. What it cannot do is compute a signature (an HMAC, for example) at send time.

Is my API key safe? It is stored encrypted by Apify, sent only to the URL you set, never written to the run log, and never written into the output. A dry run deliberately reports header names only, not values.

Will it duplicate records if I run it twice? That depends on your API, not on this Actor. Use PUT or PATCH against a per-row URL such as /contacts/{{id}} if your API treats that as an upsert. This Actor sends what you tell it to send, once per row, per run.

Can I stop response bodies being stored? Yes. Set Response characters to keep to 0. Nothing from the response body is written to the dataset, which is the right setting when responses carry personal data.

Does it work with GET or DELETE? Yes, and no body is sent for those. Placeholders in the URL and query still work, so GET https://api.example.com/lookup?email={{email}} sends one lookup per row and stores each answer.

How large an input can it handle? Up to 100,000 rows per run, and up to 100 MB for a file URL. Use Maximum input rows as a cost guard while testing.

If this saved you writing and babysitting a sync script, a review on the Store page genuinely helps.

The rest of the toolkit

A common pipeline: a scraper, then Cleaner, then Filter & Transform, then Join to enrich from a sheet, then Aggregate for the weekly summary, with Diff watching what changed and Charts & Report turning the numbers into the Monday PDF. Pipeline Runner runs that whole chain in one call.

For AI agents

Pay per event, agent-payable through x402 and MCP, limited permissions, no standby. Inputs: datasetId, fileUrl or data for the rows; url, method, bodyMode, bodyTemplate, queryParams and headers for the request, all accepting {{field}} placeholders resolved per row; authType plus authSecret for Bearer, Basic or API-key auth; batchSize, rateLimitPerSecond, retries and stopOnFirstError for pacing. Set dryRun to true to get the resolved requests back without sending them. Returns one dataset item per request with status, httpStatus, attempts, responseSnippet, responseJson and error, plus a SYNC_SUMMARY key-value record. Charged per request that received an HTTP response, including non-2xx; requests that got no response at all are not charged.