# Outbound requests

> How the ApiTraffic SDK captures the API calls your app makes, links them to the inbound request, and the cases where a body or a call is not captured.

Source: https://www.apitraffic.io/docs/outbound-requests

ApiTraffic records the calls your application makes **out** to other APIs - payment providers, shipping and tax services, your own internal services - and links each one to the inbound request that caused it. A request in the dashboard then shows its downstream calls with their own timing, status, headers and bodies, so a slow or failing endpoint points at the dependency responsible.

Outbound capture is on by default in every Node SDK package and the Strapi plugin. It needs no code beyond registering ApiTraffic.

## How outbound calls are captured

The SDK hooks the two ways Node.js makes HTTP calls:

| Your code uses | Captured by | What is recorded |
|---|---|---|
| `http.request`, `http.get`, `https.request`, `https.get`, and clients built on them (axios, got, node-fetch, SOAP and SDK clients) | The `http`/`https` hook | URL, method, headers, request and response bodies, status, timing |
| Global `fetch`, and libraries built on it | The `fetch` hook | The same, including the headers actually sent on the wire |
| `undici.request`, `undici.Client` or `undici.Pool` used directly | [APM](https://www.apitraffic.io/docs/apm/overview), when installed | Timing and metadata only, no bodies |

Both hooks produce the same record, so an axios call and a `fetch` call to the same service look the same in the dashboard.

Capture is read-only. Your call is sent exactly as your code wrote it, and bodies are read from copies. If anything about capture fails, the call still goes through: you lose the record, never the request.

### Where outbound calls appear

- **Inside a request.** A call made while handling an inbound request appears on that request as an `http.out` span in its waterfall, and as an outbound record linked to it. Requests made in parallel keep their own calls, even on shared keep-alive connections.
- **Outside a request.** Calls made by background jobs, timers or startup code have no inbound request. They are still recorded as outbound requests on their own.
- **Failed calls.** A call that gets no response at all (a DNS failure, a refused or reset connection, a timeout) is shown as a failed `http.out` span on the request that made it.

Outbound traffic has its own view in each bucket. In the sample data, for example, calls from the Acme Store API to `payments.acme-pay.example` and `api.acme-freight.example` appear under each checkout.

*Outbound calls from the sample Acme Store API.*

![Outbound request list showing calls to payment, freight, tax and inventory services with method, host, status and duration](https://www.apitraffic.io/docs/images/integrations/outbound-requests.png)

## Turn outbound capture off

| To | Set |
|---|---|
| Stop capturing all outbound calls | `interceptOutbound: false`, or `API_TRAFFIC_INTERCEPT_OUTBOUND=false` |
| Stop capturing only `fetch` calls | `captureFetch: false`, or `API_TRAFFIC_CAPTURE_FETCH=false` |
| Capture smaller bodies | `maxFetchBodyLength` (bytes), or `API_TRAFFIC_MAX_FETCH_BODY_LENGTH` |

With `interceptOutbound` off, the SDK leaves `http`, `https` and `fetch` untouched. With only `captureFetch` off, an [egress firewall policy](https://www.apitraffic.io/docs/waf/policies) still applies to `fetch` calls. See [SDK configuration](https://www.apitraffic.io/docs/integrations/configuration#capture).

## When a body is not captured

A record can arrive with a body shown as not captured. This is deliberate: it happens when reading the body would interfere with your call or hold too much data in memory.

- **The body is over the size cap.** Outbound bodies are captured up to `maxFetchBodyLength`, 512 KB by default. On `http`/`https` a larger body is cut at the cap. On `fetch`, a response body is only read when the response declares a `Content-Length` within the cap; a response without one (chunked, or still streaming) is recorded without its body.
- **The body is not text.** Images, archives, `application/octet-stream` and other binary types are recorded as `[binary body omitted: …]` with their size.
- **The response is compressed** (`Content-Encoding` such as gzip or br) on an `http`/`https` call. It is recorded as `[compressed body not captured: …]`. `fetch` responses are decompressed by Node, so their bodies are captured.
- **It is a stream on `fetch`.** Server-sent events (`text/event-stream`), NDJSON and similar streaming responses are recorded with metadata only, so ApiTraffic never keeps a stream open that your code has cancelled. On `http`/`https`, server-sent events are captured as your code reads them, up to the cap.
- **The `fetch` request body is a stream, `FormData` or `Blob`.** Reading it would consume the body your call is about to send, so it is left untouched.

Once captured, outbound bodies go through the same limits as inbound ones, including `maxBodyBytes`.

## When an outbound call is not captured

- **Outbound capture is off**, through `interceptOutbound` or, for `fetch` only, `captureFetch`.
- **The call was made before ApiTraffic started.** The hooks are installed when the SDK sets up, which begins when you call the framework function (`apitraffic.express()` and so on). A call made at import time, before that, is not seen.
- **Something replaced `globalThis.fetch` afterwards.** Libraries that wrap `fetch` are fine: ApiTraffic's hook keeps working underneath, and installing it twice never stacks wrappers. A library that assigns a brand-new function to `globalThis.fetch` (some mocking and polyfill packages do) removes the hook. Set up ApiTraffic after any library that replaces `fetch` wholesale.
- **The client bypasses both hooks**, such as `undici.request` or an undici `Pool`. With [APM](https://www.apitraffic.io/docs/apm/setup) installed these still appear, with timing and metadata but no bodies.
- **The call is to ApiTraffic itself.** The SDK's own traffic to the ingest and settings hosts is never reported.

## Redaction and exclusions

Outbound records go through the same [redaction rules](https://www.apitraffic.io/docs/data/redactions) and [exclusions](https://www.apitraffic.io/docs/data/exclusions) as inbound traffic, in your process, before anything is sent. Credential headers such as `Authorization` on an outbound call are always masked.

## Related

- [SDK configuration](https://www.apitraffic.io/docs/integrations/configuration):
    Capture switches and size limits.

- [Request details](https://www.apitraffic.io/docs/traffic/request-details):
    See a request's outbound calls in its waterfall.

- [How APM works](https://www.apitraffic.io/docs/apm/overview):
    Database, cache and queue work alongside outbound calls.

- [Firewall policies](https://www.apitraffic.io/docs/waf/policies):
    Inspect and block outbound calls with egress rules.
