The n8n HTTP Request node:
auth, pagination, real patterns.
I've debugged more broken workflows at the HTTP Request node than anywhere else in n8n. Not because it's badly designed. Because most people never learn it past "paste the URL." Once you understand how auth actually works, why your JSON body keeps arriving as a string, and when to let the built-in pagination handle things, a huge class of bugs just disappears.
- What the node actually does
- Setting it up: method, URL, headers, params
- Authentication methods — and why credentials beat pasted tokens
- Request bodies: JSON, form data, raw
- Handling responses: parsing, status codes, full response
- Pagination: built-in config vs. rolling your own loop
- Common gotchas
- Real patterns that hold up
I run n8n workflows around the clock: API pulls, webhook processors, multi-step integrations. The HTTP Request node is in almost every single one of them. What's below isn't a docs rehash. It's what I've actually learned from building these in production, including the stuff that broke at 2am on a Tuesday when nobody was watching.
The HTTP Request node is n8n's universal API adapter. Most people use 20% of it and wonder why things break.
It makes HTTP calls. GET, POST, PUT, PATCH, DELETE: whatever the API needs. It handles auth, headers, query params, request bodies, and response parsing. It's n8n's universal adapter for anything that speaks HTTP.
The node returns the full response body as a JSON object by default, assuming the API returns JSON. It also exposes headers and status code if you need them. More on that shortly because ignoring status codes bites people constantly.
Where people go wrong immediately: treating it like a form they fill out once and forget. The HTTP Request node rewards knowing its options. Skip that and you'll fight it on every API that does anything slightly non-standard.
Most broken HTTP Request nodes have the same four problems: wrong auth method, missing Content-Type header, wrong body type, or ignoring the status code on the response. Fix those four things and 90% of debugging disappears.
No toy examples in this post. These are patterns from actual workflows handling real data.
Method, URL, headers: get all three right before you touch anything else
Method + URL
Pick your method and paste the endpoint URL. You can hardcode it or build it with expressions per item:
https://api.example.com/users/{{ $json.userId }}/orders
That {{ }} syntax is n8n's expression engine pulling a value from the current item. This is how you avoid hardcoding IDs and make one node work across every item in a loop.
Headers
Most APIs need at minimum Content-Type: application/json on POST and PUT requests. n8n doesn't add this automatically when you set a JSON body via the Body tab. Miss it and you get cryptic 415 or 400 errors from APIs that care about content type.
Add it manually in the Headers section:
- Header Name:
Content-Type - Header Value:
application/json
Query parameters
Use the Query Parameters section instead of appending ?key=value to the URL. It handles URL encoding for you and stays readable when you have multiple params. Expressions work here too:
- Name:
since - Value:
{{ $now.minus({days: 7}).toISO() }}
Stop pasting API tokens directly into headers. It creates two problems you don't notice until it's too late.
Authentication is where most people take a shortcut that costs them later. The shortcut: pasting an API key or bearer token directly into an Authorization header value. It works. But it causes two real problems.
First: the token shows up in execution logs. Every time that workflow runs, n8n logs the full header, which means your API key is sitting in plaintext in your execution history. Anyone with access to your n8n instance can read it. Second: when the token rotates, you have to track down every workflow that hardcoded it and update them one by one.
The right approach is n8n's credential system. Create a credential once, reference it by name, and n8n handles the injection at runtime without logging the value.
Bearer token
For APIs that use Authorization: Bearer <token> (most modern REST APIs): in the Credential section of the HTTP Request node, select "Header Auth." Set the name to Authorization and the value to Bearer YOUR_TOKEN in the credential form. n8n injects it at runtime, never logs it.
If you type a token directly into the "Add Header" field instead of using a credential, it gets logged in every execution. Use credentials. The extra 30 seconds to create one is worth it every single time.
Basic auth
For APIs that authenticate with a username and password, n8n has a built-in Basic Auth credential type. You enter the username and password in the credential form; n8n base64-encodes them and sets the Authorization header correctly. You never have to touch the encoding yourself.
Old internal tools and some webhook endpoints still use Basic Auth. It's not dead. The credential handling is the same: create it once, reference it, done.
API key in header vs. query parameter
Different APIs want the API key in different places. Some want it as a header:
- Header:
X-API-Key: your-key-here - Or:
api-key: your-key-here(varies by API)
Others want it as a query parameter appended to every request URL: ?apiKey=your-key-here. n8n's "Query Auth" credential type handles this cleanly. Set the parameter name and value, and n8n appends it to every request that uses that credential. No manual URL construction needed.
Check the API docs carefully. Some APIs accept both; many only accept one. Using the wrong location returns a 401 that looks identical to an invalid key error, which wastes time.
Predefined credentials for popular services
If you're calling a service n8n has a native node for — Notion, Airtable, Slack, OpenAI — you can often use those same credentials in an HTTP Request node when the native node doesn't expose the specific endpoint you need. Select "Predefined Credential Type" in the node, choose the service, pick an existing credential. You get the benefits of managed token injection without having to create a separate generic credential for the same service.
Credentials stay out of logs. Pasted values don't. Always use credentials in production workflows. The only exception is a throwaway test that will never run again, and even then, clean it up before you forget.
Dynamic auth tokens from earlier in the workflow
Sometimes the auth token isn't stored. It comes from an earlier step in the same workflow: an OAuth flow, a login call, a token-exchange endpoint. For those, you build the header with expressions:
Header Name: Authorization
Header Value: Bearer {{ $('Login Node').item.json.access_token }}
The $("Node Name") syntax pulls data from a specific upstream node. This is how you chain API calls where the first call fetches a short-lived token and every subsequent call uses it.
JSON, form data, raw: pick the wrong body type and the API gets garbage with no error that explains why
The Body tab has three options that matter: JSON, Form Data, and Raw. Pick the wrong one and the API gets garbage.
JSON body
For REST APIs that accept JSON. You can use the key/value UI or switch to "JSON (Raw)" and write it directly. The raw approach is cleaner for anything more than two or three fields:
{
"name": "{{ $json.name }}",
"email": "{{ $json.email }}",
"source": "n8n-automation"
}
Form data
For older APIs and file uploads. Sets Content-Type: multipart/form-data or application/x-www-form-urlencoded depending on which form type you pick. If the API docs say "POST the parameters as form fields," this is the one.
Raw body
When you need to send something that isn't JSON — XML, a CSV string, a custom format. Set the Content-Type header manually and paste or construct the body string. It's flexible but unforgiving: n8n won't validate what you're sending.
Status codes carry information. Ignoring them is how failures become silent.
Parsing JSON
If the API returns JSON, n8n parses it automatically and puts the data in $json for the next node. You can immediately reference $json.data.items[0].id or whatever path you need.
If the API returns a JSON array at the root level (common with list endpoints), enable Split Into Items in the node settings. Without it, the whole array lands as a single item and you need a separate node to split it before looping. Turn it on and each array element becomes its own n8n item automatically.
Checking status codes
By default, n8n treats any 2xx as success and anything else as an error that stops the workflow. Usually correct, but not always.
Sometimes you want to handle a 404 differently from a 500. Or you're hitting a rate-limited API that returns 429 and you want to retry instead of fail. Enable Always Output Data to stop n8n from throwing on non-2xx. You get the response regardless of status code, then check $response.statusCode in the next node to route from there.
Pair with an IF node checking {{ $response.statusCode }} and you have basic error branching without a full try/catch setup.
Getting the full response
By default you only get the body. To access headers and status code, go to Options → Response → Full Response. The output shifts to include body, headers, and statusCode. If you need to grab a rate limit header or read a Location header from a redirect, you need this turned on.
Use n8n's built-in pagination before you build your own loop. It handles more than people think.
Most APIs cap results at 100, 50, or even 20 per page. If you need all the data, you have to paginate. This is where a lot of people build unnecessary complexity: a Loop node, a counter, manual cursor tracking, the whole thing. n8n has built-in pagination support in the HTTP Request node. Most of the time you should use that instead.
n8n's built-in pagination
Under Options → Pagination, you get three modes:
Offset-based pagination
APIs that use page or offset query parameters. You set the starting value, the increment, and the parameter name. n8n handles the loop: fires the first request, increments the offset, fires again, repeats until the stop condition is hit.
Pagination Mode: Offset-Based
Parameter Name: offset
Initial Value: 0
Increment: 100
Complete When: response body is empty
OR: item count is 0
OR: a specific field equals null
The "Complete When" condition is what stops the loop. Get it wrong and you either miss the last page of data or loop until n8n hits its execution timeout. Check what the API actually returns when there are no more results: some return an empty array, some return a flag field, some just return fewer items than the page size.
Cursor-based pagination
APIs that return a next_cursor, next_page_token, or similar field in the response body. You point n8n at the field containing the next cursor, and it follows that cursor automatically until it gets a null or empty value.
Pagination Mode: Cursor-Based
Cursor Parameter Name: cursor
Cursor Value: {{ $response.body.next_cursor }}
Complete When: {{ $response.body.next_cursor }} is empty
This is the cleanest pattern for modern APIs. GitHub, Notion, Slack, and most well-designed REST APIs use cursor-based pagination. The cursor value is opaque: you don't need to understand what it encodes, just pass it back unchanged.
Link header pagination
Some APIs follow RFC 5988 and return pagination links in the response headers. The Link header contains rel="next", rel="prev", etc. n8n can follow these automatically. Select "Response Contains Next URL" mode and point it at the header value.
When to skip built-in pagination and roll your own
The built-in pagination handles a lot of cases, but there are times it won't fit:
- You need to process results from each page before fetching the next. Built-in pagination collects everything first, then passes it downstream. If you need to do work mid-pagination (deduplication, early stopping based on data content, writing to a database page by page to avoid memory issues), you need a Loop node and manual cursor management.
- The pagination scheme is non-standard. Some APIs paginate through headers you have to parse, through session tokens, or through mechanisms that don't map cleanly to offset or cursor modes.
- You need granular error handling per page. Built-in pagination treats the full paginated fetch as one operation. If page 3 of 10 returns a 429, you can't easily resume from there. A manual loop gives you control over retries per page.
For most use cases ("get all records from this list endpoint"), built-in pagination is the right call. Less code, less canvas clutter, less to maintain.
Use built-in pagination when you want all pages collected before downstream processing. Roll your own loop when you need to act on each page, handle per-page errors, or deal with a non-standard pagination scheme.
Four problems that look mysterious but have obvious, boring fixes
SSL certificate errors
Calling an internal API or a self-signed cert endpoint? You'll hit SSL errors. The node has an option to ignore SSL certificate errors, in the node settings. Not appropriate in production against external APIs, but fine for internal tools on a private network.
Timeouts on slow APIs
The default timeout is 300 seconds. If you're hitting a slow external service, the workflow hangs and eventually times out with an unhelpful error. Lower the timeout in node settings and handle the error more gracefully rather than letting n8n wait indefinitely.
Binary data and file downloads
If the API returns a file (PDF, image, CSV download), enable Response Format → File. Without it, n8n tries to parse binary data as JSON and produces garbage. With it, the binary flows through cleanly and you can write it to disk, attach it to an email, or upload it elsewhere.
The JSON body that arrives as a string
You set up a JSON body, the request fires, and on the receiving end the body arrives as a string — something like "{ \"name\": \"test\" }" instead of an actual object. This almost always means one of two things: you didn't set the Content-Type: application/json header, or you're in the key/value body UI but the value itself is a stringified JSON expression. Switch to JSON Raw mode and set the Content-Type header. Both of those together almost always fix it.
Four patterns that cover the vast majority of real HTTP integrations
Pattern 1: Fetch + Transform + Push
HTTP Request (GET data) → Code or Set node (reshape or filter) → HTTP Request (POST to destination). This is 80% of integration workflows. Get comfortable with this pattern and you can wire up almost anything.
Pattern 2: Conditional API calls
IF node checking a condition → HTTP Request on the true branch only. You only hit the API when the condition is met. Saves quota and avoids sending junk data to endpoints that don't need it.
Pattern 3: Paginated fetch into batch processor
HTTP Request with built-in pagination enabled → Split In Batches node → process N items at a time. Handles large datasets without blowing memory or slamming downstream rate limits all at once.
Pattern 4: Error routing
HTTP Request with "Always Output Data" enabled → IF node on statusCode → success branch and error branch. Log errors to a Google Sheet or Airtable, continue the workflow on success. This is how you build workflows that don't silently discard failed API calls. The difference between "it's probably fine" and "I know what failed and why" is this four-node pattern.
Once it clicks, it clicks permanently. Every API starts looking the same.
The HTTP Request node rewards knowing it properly. The first time is the investment. After that you're faster at it than almost anything else in n8n because every API speaks the same language, and you now know how to listen.
The subtle thing nobody tells you: once you understand how auth, body types, and status codes actually work in n8n, you stop fighting this node. You stop having mysterious 400 errors at 2am. You stop copy-pasting tokens into places they don't belong. The tool becomes what it was always supposed to be: a way to connect anything to anything, without the friction.
The n8n pack has 14 workflows using these patterns for real tasks: API pulls, data enrichment, webhook processing, multi-step integrations with real authentication. Actual workflow JSON files you import and run. If you want to see how the pieces connect before you build your own, that's where the files are.
If your bigger problem is not "how do I make one request work" but "how do I stop this stack from failing silently when auth drifts, responses change shape, or retries go feral," that is an architecture problem, not a single-node problem. In that case, the See Database Suite MCP → is the better next step.
See these patterns in real workflows
14 production-ready workflows using HTTP Request nodes with real auth, error handling, and pagination patterns. Import and run. No blank canvas required. If the workflow already exists and feels fragile, use the audit to map the weak spots before you bolt on more nodes.
See Database Suite MCP →One-time $97 · Instant download · 30-day money-back guarantee · Need diagnosis instead?