Unity Ads API 403 Invalid Credentials: how to fix it
A service account key can authenticate and still get 403 Invalid Credentials. The cause is a missing organization role. Here is which role each job needs.
Unity APIBulk launch5 min read

The Advertiser Public API replaced /advertise/v1 in September 2026. New base URL, UUID app ids, microdollars and renamed fields, plus a migration order.

In September 2026, Unity’s Advertiser Public API replaced the Advertising Management API at /advertise/v1. If your scripts or tools still call the old API, you need to migrate: the base URL, app ids, money format and many field names are different. The docs we read give no shutdown date for the old API, so schedule the work instead of postponing it.
According to Unity’s What’s new in User Acquisition page, the Advertiser Public API replaces the Advertising Management API (/advertise/v1), and existing integrations should migrate. Unity’s API campaign management page calls /advertise/v1 the previous version of the advertiser API. It says the new API manages apps, campaigns, bids, source bids, ROAS goals, budgets, creatives and creative packs, and that you find it in the dashboard under Settings > API management.
The sections on URLs, ids, money, routes, fields, removals and migration order come from Unity’s Advertiser API migration reference. The page shows “Last updated 2 months ago” instead of a date, and its lists of routes and fields are selected examples. Read it in full before you port code.
The host stays services.api.unity.com. The base path changes:
| Environment | New base URL |
|---|---|
| Production | https://services.api.unity.com/ads/advertiser |
| Staging | https://staging.services.api.unity.com/ads/advertiser |
Two path changes follow. The prefix /advertise/v1 becomes /public/v1, and the segment apps/{campaignSetId} becomes applications/{applicationId}. The page gives this example of a new route:
https://services.api.unity.com/ads/advertiser/public/v1/organizations/{orgId}/applications/{applicationId}/campaigns
The old app id was campaignSetId, a 24-character hex string. The new one is applicationId, a UUID. Campaign, creative and creative pack ids stay 24-character hex and do not change.
Store a mapping from each legacy app id to its new applicationId before you port anything else. Every later step uses it. The page also tells you to verify whether creative ids survive a re-upload, so do not assume they do.
Money moves from dollar decimal strings to microdollars: integers, where 1 USD is 1,000,000. ROAS goals change shape as well.
| Value | Legacy | New |
|---|---|---|
| Amount | "2500.20" |
"2500200000" |
| Bid | "0.201" |
201000 |
| ROAS | goal: "12.34" (percent string) |
target: 0.1234 (decimal ratio) |
The page’s examples show a string for an amount and a plain integer for a bid, so confirm the type of each field in the reference before you send it. A small converter for the legacy values:
// Legacy dollar string -> microdollars as an integer string.
// Round after multiplying so floating point leaves no fraction.
const toMicros = (usd) => String(Math.round(parseFloat(usd) * 1_000_000));
toMicros("2500.20"); // "2500200000"
toMicros("0.201"); // "201000" (Unity's example shows the bid as the number 201000: use Number() if the field needs it)
GET …/apps becomes GET …/applications.GET …/organizations/{orgId}/campaigns?appIds={applicationId}.cpi-bids, source-bids, roas-bids, event-optimization-bids) become one PATCH …/campaigns/{campaignId}/bids.PATCH …/campaigns/{campaignId}/budget-cap and PATCH …/campaigns/{campaignId}/targeting.PATCH …/assign-creative-pack and DELETE …/unassign-creative-pack.GET campaign?includeFields=….The bids route needs care. It replaces the full bid set of that type, so you send the complete array, and [] clears the set. A script that raises every bid by 10% has to read the current set first, change each value, and send all of it back.
| Legacy | New | Note |
|---|---|---|
store |
store + platform |
platform is required |
goal |
optimizationType |
installs becomes cpi, eventOptimization becomes appEventConversion |
scheduleStart / scheduleEnd |
start / end |
|
enabled |
paused |
meaning is inverted |
biddingStrategy |
bidConfigurationMethod |
automated becomes auto |
cpiBids[] |
manualBids[] |
|
sourceBids[] |
manualSourceBids[] |
|
eventOptimizationBids[] |
appEventBids[] |
Watch enabled in particular. enabled: true becomes paused: false, so a straight rename would flip the state of every campaign you write.
The page lists these as removed or without an equivalent:
limitedAdTracking, connectionType, screenSize, screenDensity, allowedDevices and osMax.adomain is gone.squareVideo creative type is not supported.Per the migration reference, HTTP Basic with a service account key still works, and a Bearer JWT (issuers unity and ads) is also accepted. The credential is the same, but the required roles differ. The page says a 403 with the same credential means a missing role grant, not a request bug.
Unity’s page on locating advertiser credentials adds that only Organization Owners or Organization Managers can create service accounts and secret keys. The dashboard shows the Secret key once. We cover the role names and a checklist in Unity Ads API 403 Invalid Credentials: how to fix it.
The migration reference suggests this order:
optimizationType and start only, then PATCH the rest.The page also gives a staging base URL, listed above, if you want to run steps against it first.
DarkCore Ramet works through the official Unity API. You connect Unity orgs with a service account key or your own Unity ID, pick apps and geos once for bulk launch, and change bids, daily budgets or pause state across campaigns with bulk edit. Each change is read back from Unity before it counts as done and is logged with who made it.
/advertise/v1. The pages we read say the new API replaces the old one and that you should migrate. They give no timeline for when the old API stops working.Keep your Unity Ads bulk work on the official API
DarkCore Ramet works through the official Unity API. Connect a Unity org with a service account key or your own Unity ID, then launch and edit in bulk.
A service account key can authenticate and still get 403 Invalid Credentials. The cause is a missing organization role. Here is which role each job needs.
Unity APIBulk launch5 min read

Unity's Bulk Campaign Management uses three .xlsx sheets. It can't bulk-edit budgets or create Automated Bidding campaigns. Here is what it can do.
Bulk launchUnity API5 min read

Unity Ads allows 1 creative per second and 30 per 30 minutes to each app. Each creative in a batch counts. Here is the math for a big launch.
CreativesUnity API4 min read
