Skip to content
AT
All articles
· 3 min read · Marketo / Integrations / API

Marketo: access_token Query Param Is Out, Authorization Header Is In

Marketo is retiring token-in-the-URL authentication for its REST API. If your integration still appends ?access_token=… to every request, here is exactly what changes and the one-line fix.

Marketo has deprecated passing access_token as a query string parameter on REST API calls. Going forward, the token must be sent as a standard Authorization: Bearer header instead. Requests that still put the token in the URL will start failing once the old method is switched off.

What actually changes

Before:

GET /rest/v1/leads.json?access_token=abcd1234...&filterType=email HTTP/1.1
Host: 123-ABC-456.mktorest.com

After:

GET /rest/v1/leads.json?filterType=email HTTP/1.1
Host: 123-ABC-456.mktorest.com
Authorization: Bearer abcd1234...

Nothing about the token itself changes — you still get it from the identity endpoint the same way. The only difference is where it travels: out of the query string and into a header.

Why it matters beyond "requests will fail"

A token in the URL is not just a deprecated convenience, it is a quiet liability. Query strings get written to access logs, proxy logs, browser history and CDN caches — all in plain text. Moving to a header is a real security improvement, not just an API formality, and it is worth treating the migration as more than a find-and-replace.

The fix

If your client builds the query string manually:

// Before
$response = Http::get("https://{$host}/rest/v1/leads.json", [
    'access_token' => $token,
    'filterType' => 'email',
    'filterValues' => $email,
]);

// After
$response = Http::withToken($token)->get("https://{$host}/rest/v1/leads.json", [
    'filterType' => 'email',
    'filterValues' => $email,
]);

Http::withToken() sets Authorization: Bearer <token> for you. With Guzzle directly, it is the same idea:

$client->request('GET', $url, [
    'headers' => ['Authorization' => "Bearer {$token}"],
    'query' => ['filterType' => 'email', 'filterValues' => $email],
]);

Where to check

A handful of places tend to still have the old pattern hiding in them:

  • Shared HTTP client base config — if access_token is injected as a default query parameter for every request, that one change fixes every call site at once.
  • Webhook or bulk-export helpers — bulk extract and file-based endpoints are often written separately from the main lead API client and get missed in a quick pass.
  • Logging and error reporting — if you were ever logging the full request URL for debugging, check those logs are not the reason the token ended up in the query string in the first place.
  • Token refresh logic — unrelated to this change, but worth confirming while you're in this code: the refresh call itself still uses client_id / client_secret as query params against the identity endpoint, that part is untouched.

Bottom line

This is a small, mechanical change with a firm deadline attached. Swap the query parameter for a header on every Marketo REST call, confirm nothing downstream is still stitching access_token= into a URL, and move on — but do it before Marketo flips the switch on the old method, not after something starts silently returning 401s.

AT

Anton Tofanidze

Full-Stack Engineer · B2B marketing & enterprise platforms

Get in touch

Keep reading

Read Idempotent CRM Sync: Why Your Integration Keeps Creating Duplicates
· 3 min read

Idempotent CRM Sync: Why Your Integration Keeps Creating Duplicates

Every retry in a CRM integration is a chance to create a second copy of the same lead. Here is the key design that makes retries safe — and the three places teams us...

Integrations Salesforce RabbitMQ
Read Dashboards That Stay Fast: Rollup Tables Over Raw Events
· 3 min read

Dashboards That Stay Fast: Rollup Tables Over Raw Events

A marketing dashboard querying raw event tables is fine at 100k rows and unusable at 40 million. Pre-aggregation is the boring fix that works — here is how to struct...

Performance MySQL Laravel