Well, you are trying to send up to two million rows from ADLS Gen2 or Unity Catalog to an API that accepts no more than 500,000 rows per request, permits up to 20 requests, requires the first request to use POST and the following requests to use PUT, expects a specific JSON envelope containing metadata and nested rows and must be called from an ADF-controlled process.
ADF Copy Activity with a REST sink is the scalable ADF-native option for batching. It supports POST, PUT and PATCH, and writeBatchSize controls the number of records sent in each request. So we made distinction is between batching records and constructing an arbitrary HTTP body when the REST sink serializes each batch as a top-level JSON array:
[
{ "field1": "a", "field2": "b" },
{ "field1": "j", "field2": "k" }
]
Schema mapping can change the fields inside each record, but it does not turn natively the REST sink into a raw-file pass-through client. We must first confirm whether the downstream API can accept this array format instead of requiring the exact dataload envelope shown in the question and if the API can accept the REST sink format, I suggest you to implement the pipeline as in the following steps:
- Partition the source into deterministic batch files. Each file must stay below both the API's row limit and its byte-size limit. Treat 500,000 rows as a ceiling, not necessarily the initial production batch size.
- Keep a small manifest containing sequence number, file path, row count and a stable batch ID.
- Process the first file with a Copy Activity whose REST sink uses requestMethod: POST.
- After that activity succeeds, process the remaining files with a second Copy Activity configured with requestMethod: PUT.
- Put the PUT activity inside a sequential ForEach (isSequential: true) if the API requires ordered delivery.
- Set writeBatchSize so that one source file produces one API request. Start with a smaller tested value, for example 10,000 or 25,000 rows, and increase it only after measuring payload size and API processing time.
- Add a stable batch/correlation identifier and make the PUT operation idempotent if possible. Otherwise an ADF retry after an uncertain response could create duplicate processing.
- Record the batch ID and API acknowledgement before advancing the manifest checkpoint.
Caveat: do not use Lookup to materialize the two million rows before Web Activity. Lookup returns at most 5,000 rows and 4 MB, so that design fails before it becomes a scalable transfer mechanism.
Regarding the Content-Type error: avoid defining Content-Type in more than one place. First test the REST sink without overriding it and inspect the actual request received by the API. If the API accepts only the literal value application/json;charset=UTF-8, while the connector sends application/json, this is an API-contract compatibility issue rather than a batching issue.
If the exact nested dataload document is mandatory and the API cannot accept the REST sink's JSON array, I would not try to assemble a 500,000-row request with Lookup, variables or Web Activity. In that case there is no reliable ADF-only streaming solution for this contract. The scalable alternatives are:
- modify the API to accept the REST sink batch format;
- let the API accept a secure ADLS URL or manifest and read the file itself; or
- have ADF orchestrate an approved Function, Databricks job or other controlled component that streams each prepared file as the raw request body. In larger enviroments prefer this one for better observability ;-)
Because your environment restricts those compute options, the clean outcome may be a carefully scoped security approval for that component or downstream API contract.
Microsoft documentation:
I hope this is helpful. So please upvote the answer if the information helped you.
And please note: This answer was partially prepared and translated into English with assistance from AI (ChatGPT/Mistral AI). I reviewed and validated the technical suggestions against previous experience and the Microsoft documentation linked above.
Ciao