Logging Third-Party API Calls Without Exposing Sensitive Data
Redact API responses and error messages before they hit your logs or face a data breach.

Logging third-party API calls without leaking sensitive data is a solvable problem, and most teams solve it badly. The pattern is always the same: someone adds a debug line to trace a request, that line captures a credit card number or a JWT, and now your logging platform (the tool that's supposed to help you sleep at night) is the reason you're awake at 3am talking to a lawyer.
Here's the thing nobody wants to say out loud: logs are necessary. You need them for debugging, for auditing, for figuring out what happened when a customer calls screaming that their order vanished. But the second you write sensitive data into a log file, that log file becomes a second copy of your database, except with worse access controls and better replication. Congratulations, you've built a shadow database and nobody approved it.
Third-party API calls make this worse because the data crosses a boundary you don't fully control. You're trusting a vendor's response shape, their error messages, their habit of returning way more fields than you asked for. And most of the time, sensitive data doesn't land in your logs because someone was careless in some dramatic, fireable way. It lands there because a developer did something completely reasonable.
Picture this: a request is failing intermittently, nobody knows why, and an engineer adds console.log(JSON.stringify(requestBody)) to see what's actually being sent. That request body has a credit card number in it. Or maybe someone logs the entire request object instead of picking specific fields, and the object happens to include an authorization header. Or an exception gets thrown, and the stack trace includes the user object that triggered it, email and all. None of this is malicious. All of it is a liability the moment it hits disk.
And once it's logged, it doesn't stay put. Centralized logging platforms index it, replicate it, cache it across your observability stack, sometimes with more people having access to Datadog or Splunk than to the production database itself. The 2023 fintech case is the textbook example here: a company got fined €2.3 million under GDPR after customer transaction data turned up unredacted in application logs, accessible to third-party monitoring vendors. The API call itself wasn't the problem. What the logs recorded about that call was the problem.
What the current threat data says about API exposure at scale
Zoom out and the scale gets uncomfortable fast. The average enterprise runs on 1,558 cloud services, each one talking through multiple APIs. That's not one attack surface. That's hundreds, stacked on top of each other, most of them logging something somewhere.
A large share of organizations admit they can't fully track sensitive data across their APIs. Sit with that for a second, because it's not really a tracking problem, it's a capture problem: you can't track data you never logged cleanly to begin with. If your logs are a mess of unstructured strings and dumped objects, tracking is a fantasy.
Salt Security's 2025 report backs this up from another angle: 99% of organizations hit some kind of API security issue in the past year, and 34% of those involved sensitive data exposure or a privacy incident. That's not a rare event. That's Tuesday.
The number that should actually worry you is this one: Research suggests 38% of organizations found out about their API breaches only after someone outside the company told them. A journalist, a researcher, a customer posting screenshots on Twitter. That means their internal logs either missed the signal entirely or buried it under so much noise nobody could find it. Good logging isn't a compliance checkbox. It's the difference between your team catching the fire and a reporter catching it for you.
For what it's worth, API-related breaches have been reported to average $4.88 million in cost in 2024. Add a logging-specific angle (regulatory fines, vendor exposure, the reputational hit of "we had the data and just left it lying around") and that number climbs higher. But the two stats worth remembering are the 70% tracking gap and the 38% external-discovery rate. Everything else is noise.
The real-world cases where logged API data caused the breach
Three cases make the mechanism clear, and none of them require a villain.
The US Treasury and BeyondTrust incident in December 2024 started with a single compromised API key. That key gave attackers enough access to reset passwords in a third-party remote support tool. No key rotation. No scoped permissions. No anomaly detection watching for a credential suddenly doing something it never does. If that key had ever been written into a debug log, an error trace, or a configuration dump, its exposure surface would have grown even further, because now it's not just sitting in a vault, it's sitting in a searchable index somewhere.
Then there's the Rabbit R1 device, June 2024. API keys for ElevenLabs, SendGrid, and Azure were hardcoded directly into the device's source code. Hardcoding is really just logging's more permanent cousin: both treat a credential as a plain string instead of a secret that needs guarding. Attackers who found those keys could manipulate device responses and reach user data, all because a string sat in the open where it should have been locked away.
And GitHub's secrets exposure problem, reported in March 2024, put a number on how common this actually is: 13 million API secrets exposed through public repositories. Wiz's State of Code Security Report for 2025 found 61% of organizations have secrets sitting exposed in public repos right now. Different output destination, same root failure as sloppy logging: somewhere in the workflow, a credential got treated like ordinary text.
The through-line in all three: nobody set out to leak anything. The credential just got handled casually at some point, and logging is one of the most common places that casual handling happens.
What OWASP's current standard says about third-party API consumption and data exposure
OWASP's API Security Top 10 (2023 edition, still the standard as of 2026) has quietly shifted its framing in a way that matters here.
API3:2023, Broken Object Property Level Authorization, absorbed the older "Excessive Data Exposure" category. The root issue: APIs return entire objects without checking whether the caller should see every field in them. So if your team logs the full response from a third-party API, and that response includes fields the caller had no business receiving, you've now logged them too. The fix starts upstream, with the API only returning what's needed. But your logging layer is the last line of defense when the upstream service doesn't cooperate.
API10:2023, Unsafe Consumption of APIs, replaced the old "Insufficient Logging & Monitoring" slot entirely. That's a meaningful swap. OWASP is telling you that trusting a third-party API is now a first-class threat, not a monitoring afterthought. When you integrate someone else's API, you inherit their security posture, warts and all. Your logging strategy has to account for data that vendor sends back that you never asked for and didn't expect.
Worth noting: BOLA (API1:2023, Broken Object Level Authorization) has been reported to appear in roughly 40% of all API attacks and has held the number one spot since 2019. It's relevant here because broken object-level authorization is often exactly what causes those over-broad API responses that then get logged in full, no filtering, no second thought.
The practical read: OWASP's framework asks for server-side discipline, return minimal fields, but that's a separate control from logging discipline, record minimal fields. Teams often solve one and assume they've solved both. They haven't.
Deciding what a third-party API log entry actually needs to contain
Here's the governing rule, and it's simple enough to tattoo on a whiteboard: log metadata, not payload. You want to answer who called what, from where, and what happened, without ever recording the actual values that traveled through the pipe.
A minimum viable log entry for a third-party API call needs:
- Timestamp, ISO 8601 with UTC offset, so you can reconstruct event order across distributed systems without guessing at time zones.
- HTTP method and endpoint, like
GET /users/42orPOST /orders, which tells you what was attempted without touching the body. - Caller identity, meaning a user ID, client ID, or token issuer, never the token value itself.
- Response status and latency, enough to diagnose a failure or an SLA breach without opening the response content.
- A correlation ID, a generated request identifier that links log lines across services, so nobody's tempted to use an email address as the linking key.
And an error type and message, scoped to the class of failure rather than a full stack trace stuffed with user-identifying context.
What never belongs in that entry, no matter the environment: raw request or response bodies, authorization headers or token values, names, emails, phone numbers, credit card numbers, SSNs, healthcare identifiers, or internal IDs that map back to a person without access controls in place.
AWS says this plainly in its own documentation: turning on data tracing in API Gateway "can result in logging sensitive data," and the company recommends against it for production APIs. That's the platform vendor itself telling you full tracing belongs in development, not production. If AWS is hedging, you probably should too.
The trap that catches teams off guard: a JSON serializer will happily dump an entire request object if a developer logs the object reference instead of picking fields by hand. log.info(requestObj) looks harmless. It isn't. The schema has to enforce field selection at the structural level, because leaving it to whoever writes the log statement that day is how you end up back at square one.
Field redaction and masking: three techniques and when to apply each
Three techniques cover almost every case you'll run into. Which one you reach for depends on two questions: does this value have any debugging use, and do you need to correlate it across log lines later?
Masking shows part of a value and hides the rest. Use it when the value has real debugging context, like the last four digits of a card number confirming which card a customer used, without exposing the full number. alice@example.com becomes al@.com. A card number becomes a partially obscured string showing only the last four digits. The catch: masking still leaves something readable in the log. If the field offers no debugging value at all, skip masking and redact instead.
Redaction wipes the value out entirely and replaces it with a marker. Use it when the value has zero debugging utility and its mere presence, in any form, creates risk. hunter2 becomes [REDACTED]. An API key becomes [REDACTED]. This one isn't optional for passwords, API keys, session tokens, full SSNs, or full card numbers. There's no version of "just this once" that ends well here.
Hashing runs the value through a one-way, deterministic transformation. Use it when you need to know whether the same user or entity shows up across multiple log lines, without ever storing the readable identifier. alice@example.com becomes something like sha256:2f3a4b…, which lets you answer "did this user appear in these logs?" without her email sitting there in plain text. One catch worth repeating: emails and SSNs don't have much entropy, so an unsalted hash is reversible with a rainbow table. Salt it, or skip hashing and redact if you don't actually need correlation. Hashing is honestly underused given how well it fits audit and correlation work.
Apply all of this before the log leaves the building, meaning synchronously, in-process, before any network call ships the line off to your logging platform. Redacting at the log shipper or aggregator is better than nothing, but the value has already crossed a network hop by then, which is one hop too many. If source-level control genuinely isn't possible, shipper-level redaction is your floor, not your goal.
For finding these fields in the first place, combine two approaches. Field-name detection is fast and reliable for structured data: flag anything named password, token, ssn, card_number, or authorization automatically. Pattern matching with regex catches PII hiding inside string values, like a credit card number embedded in a free-text error message. Field-name rules handle your schema. Pattern matching catches what slips through the cracks in unstructured text.
Tokenization as an alternative to redaction for values that need to persist
Redaction has a blind spot: sometimes you genuinely need to look up the original value later. Fraud investigation, customer support, a regulatory audit. You can't just delete the thing and call it a day, but you also can't leave the raw value sitting in a log file.
Tokenization splits the difference. At ingest, the sensitive value gets swapped for a randomly generated, meaningless token, and the real value goes into a separate vault with tight access controls. The log only ever holds the token. Without vault access, that token is just noise. Authorized investigators can resolve it back to the original value through a controlled lookup, and because vault access is itself logged, resolving a token leaves its own paper trail. Nice bit of symmetry there.
So how do you choose between redaction and tokenization? Ask whether the value has any future use. If the answer is no, delete it for good, that's redaction. If the answer is maybe, a chargeback investigation, a compliance audit down the road, tokenization gives you a resolvable reference instead of a dead end.
There's a PCI DSS angle worth knowing too: tokenization is a recognized method for pulling card data out of scope entirely. A tokenized card number in a log generally doesn't pull that log storage system into PCI scope, though a masked number might, depending on how your assessor reads it. Worth a conversation with whoever handles your compliance audits.
One caution: tokenization means standing up a vault and accepting some latency. That's a fair trade for high-value identifiers like card numbers or government IDs. It's overkill for every field in every log line. Resist the urge to tokenize everything just because you built the infrastructure once. Sometimes plain redaction is genuinely the right, boring answer.
Structured log schemas that enforce field discipline at the point of writing
Here's the failure mode that undoes everything above: every developer decides, on the fly, what goes into their own log statement. Multiply that by a team of thirty engineers over three years, and your log format has drifted into chaos, with sensitive fields sneaking in wherever someone needed a little extra context at 2am.
A structured log schema fixes this by defining the allowed fields upfront. The log object has typed slots (timestamp, method, endpoint, status, correlation ID) and nothing outside those slots gets written, full stop. It's not a style guide. It's enforcement.
This works because it removes the decision from the individual developer in the moment. Nobody has to remember, mid-incident, whether it's okay to log the request body "just this once for debugging." The schema already answered that question, months earlier, when someone with the full picture designed it. That's the whole point: good judgment applied once, at design time, beats good judgment hoped for every single time someone writes a log line under pressure.
Pair that schema with the redaction and tokenization rules from earlier, and you've built something closer to a pipeline than a habit. The developer writes to the schema. The schema enforces the fields. The redaction layer scrubs what shouldn't be there before it ever leaves the process. None of it depends on someone remembering to be careful at the exact moment they're least likely to be.


