Complete "Lesson 4: Adding Logic, Conditions, and Error Handling" first to unlock this lesson.Go to previous lesson

Introduction

In n8n, most workflows can be built using native nodes — each one a wrapper around a specific service. But real business automation rarely stays inside neat boundaries. You will hit cases where the service you need has no dedicated node, or its node lacks a specific endpoint, or you need to transform data in a way that built-in nodes make painful. That is when HTTP Request nodes and Code nodes take over. This lesson focuses on those two tools: how to call any external API, and how to drop into custom JavaScript or Python when n8n's visual nodes are not enough.

Calling External APIs with HTTP Request

The HTTP Request node is your universal connector. It speaks REST out of the box, and it works with any service that exposes an HTTP endpoint — even ones that have no n8n node at all.

A typical GET request to a public API looks like this in n8n's node config:

bash
curl -X GET "https://api.example.com/v1/customers/42" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"

In the HTTP Request node UI, you set Method to GET, paste the URL, and add the two headers under Headers. The node returns the response body as a JSON object that downstream nodes can reference using n8n expressions like `$json.fieldName`.

For POST requests sending data, switch the Method, set Send Body to JSON, and provide the body. For example, creating a record in an external CRM:

json
{
"name": "Acme Corp",
"email": "contact@acme.com",
"source": "n8n-workflow"
}
diagram showing the data flow from a trigger node through an HTTP Request node to an external API and back into downstream n8n nodes

Authentication options matter. Many business APIs use Bearer tokens, OAuth2, or API keys passed as query parameters. n8n supports all of these as credential types, so you never hardcode secrets inside the node — reference them through the Credentials dropdown. For services using a custom auth scheme, set Authentication to Generic Credential Type and provide header names and values that reference stored credentials.

When to Reach for Code Nodes

Built-in nodes cover the common 80% — loops via SplitInBatches, data mapping via Set nodes, conditional logic via IF nodes. The remaining 20% is where Code nodes earn their place: complex string parsing, statistical calculations, calls to helper libraries, date math that n8n's Date node handles awkwardly, or any case where chaining five visual nodes is slower to build and harder to read than ten lines of code.

The Code node has two modes. JavaScript mode runs natively inside n8n's Node.js runtime and gives you full access to npm packages already installed. Python mode uses Pyodide or a hosted Python worker depending on your n8n setup.

A common use case: cleaning up messy data arriving from a webhook before saving it. Suppose a form sends country names in mixed casing with trailing spaces, and you need ISO country codes for an external lookup:

javascript
const items = $input.all();
const countryMap = {
"united states": "US",
"united kingdom": "GB",
"vietnam": "VN",
"germany": "DE"
};
return items.map(item => {
const raw = (item.json.country || "").toLowerCase().trim();
const code = countryMap[raw] || "UNKNOWN";
return {
json: {
...item.json,
country_code: code
}
};
});

The equivalent in Python mode would look nearly identical, swapping syntax for dictionary access and list comprehensions. The same node handles both languages — pick whichever your team reads more comfortably.

decision flowchart showing when to use a native node versus an HTTP Request node versus a Code node

Combining HTTP Request and Code in One Workflow

The real power shows up when you combine them. A common pattern: a Code node transforms incoming data, an HTTP Request node calls an external service for enrichment, a second Code node post-processes the response, and the result flows to your final destination.

Example flow — invoice enrichment:

javascript
// Code node 1: normalize vendor names
const items = $input.all();
const cleaned = items.map(i => ({
json: {
invoice_id: i.json.id,
vendor: i.json.vendor_name.toLowerCase().replace(/[^a-z0-9]/g, "-")
}
}));
return cleaned;

Then an HTTP Request node hits your accounting API at `GET /vendors/{vendor}` to pull payment terms. Finally a Code node merges results:

javascript
const items = $input.all();
return items.map(i => ({
json: {
invoice_id: i.json.invoice_id,
vendor: i.json.vendor,
payment_terms: i.json.payment_terms || "NET30",
flagged: !i.json.payment_terms
}
}));

That third node also flags invoices where payment terms came back empty — a small bit of business logic that would require extra IF nodes if done visually.

Common Mistakes

A few pitfalls worth flagging before you build. First, never put credentials directly inside the node body or URL — always use n8n's credential system, otherwise secrets end up in workflow exports and version control. Second, the HTTP Request node defaults to "JSON response" — if your API returns XML or plain text, switch Response Format or you will see parse errors. Third, Code nodes process items one batch at a time; if you need state across executions (counters, caches), that is what a database or n8n's static data is for, not a Code node. Fourth, be careful with `await` inside Code node JavaScript — it is supported but requires wrapping in an async function, which trips up many first-time users.

Summary

HTTP Request nodes give n8n a universal way to talk to any HTTP API. Code nodes fill the gaps where visual logic gets unwieldy or impossible. The patterns above — external API calls, data transformation, and combining both — cover roughly nine out of ten custom integrations you will build in production. Master these two node types and you stop being limited to what service-specific nodes happen to support.

Lesson practice

Lesson Checkpoint

Review what you learned and get feedback on your work.

0/1
Answered · 0/1