Illustrative — the HTTP Request in the center communicates with any service; pagination processes lists in batches.
🌐 HTTP Request and REST APIs
O HTTP Request node is the Swiss Army knife: when there’s no native node, it talks to any REST API. You define the method, URL, headers, and body, and receive the response as an item.
GET https://api.exemplo.com/v1/pedidos
?status=={{ $json.status }}&limit=50
// a resposta vira item; acesse com: ={{ $json.data }}
📊 The REST verbs that matter
- GET - read data (doesn't change anything on the server).
- POST - create a new record.
- PUT/PATCH - update an existing record.
- DELETE - remove. Always check the status code: 2xx success, 4xx your error, 5xx their error.
🪪 Authentication
The error 401 Unauthorized is the #1 integration pitfall. The API needs proof of who you are: a token in the header (Bearer) or a consent flow (OAuth2).
Authorization: Bearer ={{ $env.API_TOKEN }}
Content-Type: application/json
✓ Bearer token
- ✓Simple: 1 fixed key in the header.
- ✓Good for server APIs (OpenAI, Stripe).
- ✓Store the key in $env/credential.
✓ OAuth2
- ✓To access a user's account (Google, Slack).
- ✓n8n refreshes the token automatically (refresh).
- ✓Configure it once in the credential and reuse it.
⚠️ Got a 401? Check in this order
Correct key? Header with the exact name (Authorization)? Prefix Bearer with room? Expired token (OAuth)? Sufficient scope? 90% of 401s happen here.
🗄️ Supabase / database: read and write
The database is the memory of automation. Without it, every run forgets everything. With Supabase/Postgres, you read with SELECT and write with INSERT/UPDATE—or better, UPSERT, which records or updates by unique key.
POST https://abcd.supabase.co/rest/v1/leads
Headers:
apikey: ={{ $env.SUPABASE_SERVICE_KEY }}
Prefer: resolution=merge-duplicates
Body:
{ "email": "={{ $json.email }}", "intencao": "={{ $json.intencao }}" }
🔑 Unique key = your anchor
Define a unique column (e.g., email). The UPSERT uses it to decide between creating and updating—that’s what prevents duplicates (covered in module 4.4).
- •SELECT with a filter ?email=eq.ana@x.com to read.
- •Prefer: resolution=merge-duplicates performs the UPSERT in Supabase.
📊 Spreadsheets (Google Sheets)
Google Sheets is the favorite “database” of people who aren’t developers. Clients understand spreadsheets. Delivering results in a tab is the fastest way to show value without training anyone—read by row, write by append or update.
Nome: ={{ $json.nome }}
Email: ={{ $json.email }}
Prioridade: ={{ $json.prioridade }}
Data: ={{ $now.format('dd/MM/yyyy') }}
Adds a new row
Per key/column
Spreadsheet as input
Tab and range
💡 Practical tip
Spreadsheets are great for visible delivery, but a poor choice as a transactional database. For deduplication and volume, use Supabase; for the client see the result, mirror it in a tab.
🔄 Pagination and list processing
APIs limit how many items they return per call and impose rate limit. Processing everything at once breaks. SplitInBatches (Loop Over Items) splits the list into batches; a cursor/offset fetches pages one by one.
GET https://api.exemplo.com/v1/itens?cursor=={{ $json.next_cursor }}
// repita o HTTP node em loop enquanto next_cursor não for null
// SplitInBatches: tamanho 50 + Wait 1s entre lotes p/ respeitar rate limit
⚠️ The N+1 error and rate limit
Firing one call per item, with no batching or waiting, leads to 429 Too Many Requests and at an absurd cost. Always group them into batches and add a Wait between them.
Split the list
SplitInBatches with a batch size (e.g., 50) splits 500 items into 10 rounds.
Process the batch and wait
Do the batch work, add a Wait node, and return to SplitInBatches.
Stop when it runs out
The loop ends when there are no more batches (or next_cursor is null).
🧮 Handle nulls and map fields
A field unexpected null breaks the flow halfway through. n8n expressions ={{ }} with a default value and optional chaining make the automation resilient to dirty data.
// valor padrão quando vem nulo/undefined ={{ $json.email ?? 'sem-email@placeholder.com' }} // optional chaining evita "cannot read property of undefined" ={{ $json.cliente?.endereco?.cidade ?? 'N/D' }} // renomear/formatar ao mapear ={{ $json.valor ? 'R$ ' + $json.valor.toFixed(2) : 'R$ 0,00' }}
✓ Resilient mapping
- ✓Always ?? valor_padrao in a field that may be missing.
- ✓?. in nested objects from an external source.
- ✓Format types (date, number) in the mapping.
✗ Fragile mapping
- ✗Assume every item has all the fields.
- ✗Access $json.a.b.c without protection.
- ✗Record undefined in the database/spreadsheet.
📌 Module Summary
Next Module:
4.4 - Reliability (errors, retries, idempotency, observability, production checklist)