Migrate from eBay API v1 to v2 for marketplace integrations in 2025

Why Migrate from eBay API v1 to v2 Now?

EBay has officially deprecated its v1 API endpoints, and with the recent market focus on eBay as a competitive marketplace platform, developers need a clear path to migrate their integrations. Whether you're building inventory management tools, price synchronization systems, or order fulfillment automation, moving to the eBay API v2 is no longer optional—it's essential for maintaining reliable marketplace connectivity.

The v2 API introduces OAuth 2.0 authentication, RESTful architecture, and improved rate limiting that directly impact how you'll structure production integrations. This guide walks you through the exact steps to migrate without breaking your existing application.

Key Differences Between v1 and v2

Understanding the architectural changes is crucial before you start coding:

| Feature | eBay API v1 | eBay API v2 | |---------|------------|------------| | Authentication | eBay token-based | OAuth 2.0 authorization code flow | | Protocol | XML-based SOAP | RESTful JSON | | Endpoint Structure | /webservices/onsite/ebayapi/servlet/AuctionService | /sell/inventory/v1/, /sell/fulfillment/v1/ | | Rate Limits | Call quota per category | Request throttling per endpoint | | Error Handling | SOAP faults | HTTP status codes + error arrays | | Sandbox URL | api.sandbox.ebay.com | api.sandbox.ebay.com (same, different paths) |

Step 1: Register Your Application for OAuth 2.0

First, you need to obtain new credentials that support OAuth 2.0:

  1. Log in to your eBay Developer Account
  2. Navigate to App Keys under Keys & Auth
  3. Click Create an application (if you haven't already)
  4. Select Marketplace as your application type
  5. Copy your:
    • Client ID (App ID)
    • Client Secret (Cert ID)
  6. Set your Redirect URI to your server's callback endpoint (e.g., https://yourdomain.com/ebay/callback)

Store these securely in your environment variables:

EBAY_CLIENT_ID=your_client_id_here
EBAY_CLIENT_SECRET=your_client_secret_here
EBAY_REDIRECT_URI=https://yourdomain.com/ebay/callback
EBAY_SANDBOX_MODE=true  # Set to false for production

Step 2: Implement OAuth 2.0 Token Exchange

The authentication flow is dramatically different. Here's how to handle the authorization code flow:

const axios = require('axios');

const EBAY_SANDBOX_AUTH_URL = 'https://auth.sandbox.ebay.com/oauth2/authorize';
const EBAY_SANDBOX_TOKEN_URL = 'https://api.sandbox.ebay.com/identity/v1/oauth2/token';

// Step 2a: Generate authorization URL for user consent
function getAuthorizationUrl() {
  const params = new URLSearchParams({
    client_id: process.env.EBAY_CLIENT_ID,
    redirect_uri: process.env.EBAY_REDIRECT_URI,
    response_type: 'code',
    scope: 'https://api.ebay.com/oauth/api_scope/sell.inventory https://api.ebay.com/oauth/api_scope/sell.fulfillment',
    state: 'random_state_string_for_csrf'
  });
  return `${EBAY_SANDBOX_AUTH_URL}?${params.toString()}`;
}

// Step 2b: Exchange authorization code for access token
async function getAccessToken(authorizationCode) {
  try {
    const response = await axios.post(
      EBAY_SANDBOX_TOKEN_URL,
      new URLSearchParams({
        grant_type: 'authorization_code',
        code: authorizationCode,
        redirect_uri: process.env.EBAY_REDIRECT_URI
      }),
      {
        auth: {
          username: process.env.EBAY_CLIENT_ID,
          password: process.env.EBAY_CLIENT_SECRET
        },
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      }
    );
    return response.data.access_token;
  } catch (error) {
    console.error('Token exchange failed:', error.response.data);
    throw error;
  }
}

// Step 2c: Refresh token when expired
async function refreshAccessToken(refreshToken) {
  const response = await axios.post(
    EBAY_SANDBOX_TOKEN_URL,
    new URLSearchParams({
      grant_type: 'refresh_token',
      refresh_token: refreshToken
    }),
    {
      auth: {
        username: process.env.EBAY_CLIENT_ID,
        password: process.env.EBAY_CLIENT_SECRET
      },
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }
  );
  return response.data;
}

Step 3: Rewrite API Calls from SOAP to REST

This is the most labor-intensive part. Here's how to convert common operations:

Old v1 SOAP Call (GetItem):

<GetItemRequest>
  <ItemID>123456789</ItemID>
  <IncludeSelector>Details,Variations</IncludeSelector>
</GetItemRequest>

New v2 REST Call (Get Item):

async function getInventoryItem(sku, accessToken) {
  const inventoryUrl = `https://api.sandbox.ebay.com/sell/inventory/v1/inventory_items/${sku}`;
  
  const response = await axios.get(inventoryUrl, {
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Language': 'en-US'
    }
  });
  
  return response.data;
}

Old v1: ReviseItem (updating inventory)

// v1 required full item resubmission
const reviseItemRequest = {
  ItemID: '123456789',
  Item: { Quantity: 50 }
};

New v2: Update Inventory Item (granular updates)

async function updateInventoryQuantity(sku, quantity, accessToken) {
  const url = `https://api.sandbox.ebay.com/sell/inventory/v1/inventory_items/${sku}`;
  
  const response = await axios.put(
    url,
    {
      availability: {
        shipToLocationAvailability: [
          {
            quantity: quantity,
            shipToLocation: {
              country: 'US'
            }
          }
        ]
      }
    },
    {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      }
    }
  );
  
  return response.data;
}

Step 4: Handle Errors and Rate Limiting

v2 uses HTTP status codes and structured error responses:

async function makeEbayApiCall(method, endpoint, accessToken, data = null) {
  try {
    const response = await axios({
      method,
      url: `https://api.sandbox.ebay.com${endpoint}`,
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      data
    });
    return response.data;
  } catch (error) {
    if (error.response?.status === 429) {
      // Rate limited - implement exponential backoff
      const retryAfter = error.response.headers['retry-after'] || 60;
      console.log(`Rate limited. Retry after ${retryAfter}s`);
      await new Promise(r => setTimeout(r, retryAfter * 1000));
      return makeEbayApiCall(method, endpoint, accessToken, data);
    }
    
    if (error.response?.data?.errors) {
      error.response.data.errors.forEach(err => {
        console.error(`[${err.errorId}] ${err.message}`);
      });
    }
    throw error;
  }
}

Step 5: Test in Sandbox First

Never migrate directly to production. Use eBay's sandbox environment:

// Configuration
const config = {
  sandbox: {
    authUrl: 'https://auth.sandbox.ebay.com',
    apiUrl: 'https://api.sandbox.ebay.com'
  },
  production: {
    authUrl: 'https://auth.ebay.com',
    apiUrl: 'https://api.ebay.com'
  }
};

const env = process.env.EBAY_SANDBOX_MODE === 'true' ? 'sandbox' : 'production';
const baseUrl = config[env].apiUrl;

Common Migration Pitfalls

1. Forgetting to include required scopes: The OAuth token must have appropriate scopes for the endpoints you're calling. Missing scopes result in 403 Forbidden errors.

2. Not handling token expiration: v2 tokens expire (usually in 2 hours). Implement refresh token logic before making requests with expired tokens.

3. Case sensitivity in REST paths: /sell/inventory/v1 is not the same as /sell/Inventory/v1. eBay's v2 API is case-sensitive.

4. Incorrect endpoint versions: Use /v1/ endpoints consistently. Mixing v1 and v2 endpoints in the same application causes confusion.

5. Missing Content-Language header: Always include Content-Language: en-US in request headers to avoid locale-specific errors.

Migration Timeline

Plan your migration in phases:

  1. Week 1-2: OAuth setup, token management, and sandbox testing
  2. Week 3-4: Rewrite 20% of API calls and test integration
  3. Week 5-6: Dual-running (v1 and v2 simultaneously) with feature flags
  4. Week 7: Production deployment with rollback plan
  5. Week 8+: Monitor and decommission v1 endpoints

Conclusion

Migrating to eBay API v2 requires architectural changes beyond simple endpoint swaps. The OAuth 2.0 authentication, REST-based design, and improved error handling provide a more robust foundation for marketplace integrations at scale. Start in sandbox, test thoroughly, and use feature flags to manage the transition safely.

Recommended Tools

  • VercelDeploy frontend apps instantly with zero config
  • SupabaseOpen source Firebase alternative with Postgres