Marketplace APIs 9 min read

eBay OAuth: RuName Is Not a Redirect URI (Connecting a Seller Account)

Your eBay consent URL keeps failing with an invalid redirect_uri, and the value you're sending looks perfectly correct. It is — it's just the wrong kind of value. eBay expects a RuName, not your callback URL. Here's the full flow for connecting one seller account: keyset, the legal-address gate, scopes, token exchange, and the first getOrders call.

Updated Aug 2026
eBay OAuth: RuName Is Not a Redirect URI (Connecting a Seller Account)

The Error: Your redirect_uri Looks Right and Still Fails #

You build the eBay consent URL exactly the way you would for any OAuth 2.0 provider, send the seller to it, and get bounced:

The redirect_uri is invalid or does not match the registered value.

So you check your callback URL. It's registered. It's HTTPS. It matches character for character. Nothing is wrong with it — and that's the problem. eBay does not want a URL in the redirect_uri parameter. It wants a RuName.

A RuName (Redirect URL name) is eBay's alias for your callback: an opaque token you register once in the developer portal, which eBay maps internally to the real HTTPS URL. You send the alias; eBay resolves it. It looks nothing like a URL:

redirect_uri=Your_Company-YourAppI-SomeAp-abcdefgh   <-- correct (RuName)
redirect_uri=https://api.example.com/ebay/callback    <-- rejected

That single substitution fixes the majority of "I can't get eBay OAuth working" cases. The rest of this post is the whole flow around it, in the order you'll actually hit each step.

The Flow, End to End #

Four moving parts: your app's keyset, the RuName, the seller's consent, and the tokens you get back.

eBay OAuth authorization code flow: your server redirects the seller to eBay auth with the RuName, eBay returns a code to your callback, you exchange it for an access token and an 18-month refresh token 1. Your server builds consent URL 2. auth.ebay.com seller logs in + consents 3. Your callback receives ?code=… redirect_uri=RuName RuName resolved to URL 4. Token exchange Basic auth, form-encoded access token 2 h refresh token ~18 mo store the refresh token once — it does not rotate

Step 1: A Production Keyset (and Ignore Auth'n'Auth) #

In the eBay developer portal your application is a keyset, and you get separate sandbox and production keysets. A production keyset gives you three values:

  • App ID (also called Client ID)
  • Dev ID
  • Cert ID (the client secret — treat it like a password)

The portal will also offer you Auth'n'Auth alongside OAuth. Auth'n'Auth is the legacy token scheme for the old Trading API. Unless you specifically need a Trading API call, choose OAuth — it's the modern path, it gives you short-lived access tokens with a refresh token, and every current Sell API is built around it. Mixing the two up costs an afternoon.

One app, many sellers. Unlike Etsy (one app, but per-shop tokens) or Walmart (a credential pair per seller), eBay's model is Amazon-like: one shared keyset for your integration, plus a per-seller refresh token. Design your account table that way from the start.

Step 3: Scopes — Start Read-Only #

Scopes are locked in at consent time. Adding one later costs you a second trip through the consent screen for every seller you've already connected, so decide deliberately. For a read-only order sync, three are enough:

https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly
https://api.ebay.com/oauth/api_scope/sell.account.readonly
https://api.ebay.com/oauth/api_scope/commerce.identity.readonly

Writing tracking numbers back later means sell.fulfillment (without .readonly) and a re-authorisation. The full list is in eBay's OAuth scopes reference.

Check the consent screen preview. If a scope isn't enabled on your keyset, eBay doesn't always error loudly — the consent page simply won't list it, and you'll get a token that 403s later. Read the consent screen before you click through it.

Step 4: Exchanging the Code for Tokens #

The callback receives ?code=…&state=…. Verify the state, then exchange. The exchange is form-encoded with HTTP Basic auth — not a JSON body, and not client credentials in the body:

import base64, requests

basic = base64.b64encode(f"{APP_ID}:{CERT_ID}".encode()).decode()

r = requests.post(
    "https://api.ebay.com/identity/v1/oauth2/token",
    headers={
        "Authorization": f"Basic {basic}",
        "Content-Type": "application/x-www-form-urlencoded",
    },
    data={
        "grant_type": "authorization_code",
        "code": code,
        "redirect_uri": RUNAME,      # the RuName again, not the URL
    },
    timeout=30,
)
tokens = r.json()

Note that redirect_uri is the RuName here too. Sending the real URL at this step is the second place people trip on the same rule.

Token Lifetimes: 18 Months, and It Does Not Rotate #

This is the design-shaping fact. eBay's access token lasts 7200 seconds (2 hours). The refresh token lasts roughly 18 months and does not rotate — refreshing returns a new access token and leaves your refresh token unchanged.

That is the opposite of Etsy, where every refresh issues a new refresh token and invalidates the old one. The practical consequence:

Marketplace Refresh token Re-persist on refresh?
eBay~18 months, staticNo — store once at connect
Etsy90 days, rotatesYes — atomically, every time
Amazon SP-APILong-lived, staticNo
WalmartNone (client credentials)N/A

So for eBay: persist the refresh token once, cache the access token with its expiry, and re-mint only when you're inside a few minutes of expiry. Also persist refresh_token_expires_in — 18 months is far enough away that nobody remembers it, and when it lapses the seller must go through consent again. Put a warning on your account page at, say, T-30 days. That single field turns a silent outage into a calendar item.

Step 5: Which Seller Just Connected? (Watch the Hostname) #

Never trust the operator to tell you which account they just linked — resolve it from the token itself. The identity call is:

GET https://apiz.ebay.com/commerce/identity/v1/user

That is apiz.ebay.com, not api.ebay.com. The extra z is easy to miss and produces a confusing 404. It returns the username, user ID and registration marketplace, which is exactly what you want to label the connected account with. Reference: getUser.

Step 6: The First getOrders Call #

Orders come from the Sell Fulfillment API with the access token as a normal bearer:

GET https://api.ebay.com/sell/fulfillment/v1/order
Authorization: Bearer <access_token>

Four behaviours worth knowing before you rely on it:

  • No filter means the last 90 days. That's the default window, not "everything". For incremental syncs use filter=lastmodifieddate:[2026-08-01T00:00:00.000Z..].
  • Paging is limit (max 1000, default 50) plus offset.
  • One token spans every marketplace the seller trades on. EBAY_US and EBAY_GB orders come back together; lineItems[].listingMarketplaceId tells you which is which.
  • cancelStatus.cancelRequests is always empty on getOrders. It's populated on the single-order getOrder call only. If you're building cancellation handling off the list endpoint, you will see nothing and conclude, wrongly, that no cancellations exist.

Buyer name and full shipping address come back inline for the seller's own orders — there is no separate restricted-data step like Amazon's. If you're coming from SP-API, that's a pleasant surprise; see how Amazon handles the same data for the contrast. Money arrives as a decimal string ({"value": "43.19", "currency": "USD"}), not minor units — parse it, don't divide by 100. Full field list: getOrders reference.

Connecting several seller accounts at once? The auth is only half the job — see how the four big marketplace APIs differ, and how I build multi-account marketplace integrations.

FAQ #

What is a RuName in eBay's API?

A RuName is eBay's redirect-URI alias — an opaque token you register in the developer portal that stands in for your real HTTPS callback URL. You pass it as the redirect_uri parameter in both the consent URL and the token exchange; eBay resolves it to the actual URL internally.

Why does eBay reject my redirect_uri even though it is registered?

Because you are almost certainly sending the URL itself. eBay expects the RuName string in that parameter, not the HTTPS address. Swap in the RuName and the consent URL works.

How long does an eBay refresh token last?

About 18 months (47,304,000 seconds), and it does not rotate — refreshing gives you a new access token but the same refresh token. Store it once, and record its expiry so you can prompt for re-authorisation before it lapses.

Does eBay OAuth require PKCE?

No. eBay uses a plain OAuth 2.0 authorization-code grant with client authentication via HTTP Basic. That differs from Etsy, which does require PKCE with S256.

Why can't I create a RuName in the developer portal?

Usually because the "Confirm your Legal Address" step on your developer account is incomplete. The address fields are frequently empty even when the rest of the profile is filled in, and the RuName form stays blocked until they are saved.

Should I use OAuth or Auth'n'Auth?

OAuth, unless you specifically need the legacy Trading API. Auth'n'Auth is the older token scheme; every current Sell API is designed around OAuth access and refresh tokens.

Don't want to build this yourself?

I set this up for sellers and agencies every week. Book a free 30-minute audit of your Amazon data & PPC setup — you'll leave with a plan either way, whether we work together or not.

Found this helpful? Share it:

WhatsApp