# How to Integrate Replit with FedEx API

- Tool: Replit
- Difficulty: Intermediate
- Time required: 35 minutes
- Last updated: March 2026

## TL;DR

To integrate Replit with the FedEx API, register a FedEx developer account, generate OAuth 2.0 client credentials, store them in Replit Secrets (lock icon 🔒), and call the FedEx REST API from Python or Node.js to get shipping rates, track packages, and create labels. FedEx's REST API uses client credentials OAuth — your backend exchanges credentials for a Bearer token before each API session.

## Why Connect Replit to FedEx?

FedEx is the dominant carrier for US domestic express shipping, offering next-day, two-day, and ground services with real-time tracking across millions of packages daily. The FedEx REST API (launched 2022, replacing the older SOAP Web Services API) gives Replit developers access to rate quotes, package tracking, label generation, address validation, and pickup scheduling through a modern JSON-based interface.

For e-commerce applications and order management systems built on Replit, integrating FedEx enables live shipping rate calculation at checkout, automated label generation after order confirmation, proactive delivery status notifications to customers, and exception handling for delayed or lost packages. The FedEx API is particularly valuable for businesses shipping high volumes of domestic parcels where FedEx Express and Ground services provide the best coverage and reliability.

The FedEx REST API uses OAuth 2.0 client credentials authentication — your backend exchanges a client ID and secret for a Bearer token that is valid for one hour. This token-based approach is more secure than static API keys because tokens expire automatically and can be revoked individually. Store your client ID and client secret in Replit Secrets (lock icon 🔒) and implement token caching to avoid requesting a new token on every API call.

## Before you start

- A Replit account with a Python or Node.js project created
- A FedEx developer account registered at developer.fedex.com
- A FedEx business account number (required for label creation — tracking and rates work with the sandbox account)
- Basic familiarity with OAuth 2.0 client credentials flow
- Node.js 18+ or Python 3.10+ (both available on Replit by default)

## Step-by-step guide

### 1. Create a FedEx Developer App and Get Client Credentials

Navigate to developer.fedex.com and sign in with your FedEx account credentials (or create a developer account if you do not have one). Click on 'My Projects' in the top navigation and then 'Create a Project'.

Select the APIs you want to use — for a typical shipping integration, select 'Rate' (for rate quotes), 'Ship' (for label creation), and 'Track' (for package tracking). Give your project a name and complete the creation wizard.

Once the project is created, you will see your project dashboard with a Client ID and Client Secret. Copy both values immediately — the client secret may not be shown again after you navigate away. These are your OAuth 2.0 credentials.

FedEx provides a sandbox testing environment by default for new projects. Test your integration against the sandbox (with test tracking numbers and sandbox addresses) before switching to production. To enable production access, you will need to go through FedEx's certification process, which involves testing specific scenarios and submitting them for FedEx review.

Note your FedEx account number from your FedEx billing account — you will need this for creating shipments (though not for tracking or rate queries in sandbox mode).

**Expected result:** You have a FedEx developer project with a Client ID, Client Secret, and the base URLs for sandbox API testing.

### 2. Store FedEx Credentials in Replit Secrets

Open your Replit project and click the lock icon 🔒 in the left sidebar to open the Secrets pane. Add the following secrets:

- Key: FEDEX_CLIENT_ID — Value: your FedEx project client ID
- Key: FEDEX_CLIENT_SECRET — Value: your FedEx project client secret
- Key: FEDEX_ACCOUNT_NUMBER — Value: your FedEx billing account number (for label creation)

For toggling between sandbox and production:
- Key: FEDEX_ENV — Value: 'sandbox' or 'production' (your code reads this to select the base URL)

In Python, access these with os.environ['FEDEX_CLIENT_ID']. In Node.js, use process.env.FEDEX_CLIENT_ID. The FedEx client secret is sensitive — if exposed, an attacker could generate shipping labels or access your account data. Replit's Secret Scanner will alert you if credentials are accidentally written to code files.

**Expected result:** FEDEX_CLIENT_ID, FEDEX_CLIENT_SECRET, and FEDEX_ACCOUNT_NUMBER appear in Replit Secrets with masked values.

### 3. Authenticate and Query Shipping Rates with Python

The FedEx REST API requires an OAuth 2.0 Bearer token obtained by POST-ing your client ID and secret to the token endpoint. The token is valid for 3600 seconds. Cache it in a module-level dictionary with the expiry timestamp to avoid requesting a new token on every API call.

The Rate API requires a detailed request body including shipper and recipient addresses, package weight and dimensions, and the requested service types. The response lists available services with base rates and estimated delivery dates.

The code below implements a full FedEx client with token management, rate queries, and package tracking. Install the requests library (pre-installed on Replit) and you are ready to run.

```
import os
import time
import requests
from typing import Optional

CLIENT_ID = os.environ["FEDEX_CLIENT_ID"]
CLIENT_SECRET = os.environ["FEDEX_CLIENT_SECRET"]
ACCOUNT_NUMBER = os.environ.get("FEDEX_ACCOUNT_NUMBER", "")
ENV = os.environ.get("FEDEX_ENV", "sandbox")

BASE_URL = (
    "https://apis-sandbox.fedex.com" if ENV == "sandbox"
    else "https://apis.fedex.com"
)

# Token cache: stores {'token': str, 'expires_at': float}
_token_cache = {}

def get_access_token() -> str:
    """Get a valid FedEx OAuth access token, refreshing if expired."""
    now = time.time()
    if _token_cache.get('token') and _token_cache.get('expires_at', 0) > now + 60:
        return _token_cache['token']

    response = requests.post(
        f"{BASE_URL}/oauth/token",
        data={
            "grant_type": "client_credentials",
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET
        },
        headers={"Content-Type": "application/x-www-form-urlencoded"}
    )
    response.raise_for_status()
    data = response.json()
    _token_cache['token'] = data['access_token']
    _token_cache['expires_at'] = now + data.get('expires_in', 3600)
    return _token_cache['token']

def get_rates(shipper_zip: str, recipient_zip: str, weight_lb: float,
              length: int = 12, width: int = 10, height: int = 8) -> list:
    """Get FedEx shipping rates between two US zip codes."""
    token = get_access_token()
    payload = {
        "accountNumber": {"value": ACCOUNT_NUMBER or "510087240"},  # Test account for sandbox
        "requestedShipment": {
            "shipper": {"address": {"postalCode": shipper_zip, "countryCode": "US"}},
            "recipient": {"address": {"postalCode": recipient_zip, "countryCode": "US"}},
            "pickupType": "DROPOFF_AT_FEDEX_LOCATION",
            "rateRequestType": ["LIST", "ACCOUNT"],
            "requestedPackageLineItems": [{
                "weight": {"units": "LB", "value": weight_lb},
                "dimensions": {"length": length, "width": width, "height": height, "units": "IN"}
            }]
        }
    }
    response = requests.post(
        f"{BASE_URL}/rate/v1/rates/quotes",
        json=payload,
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json", "X-locale": "en_US"}
    )
    response.raise_for_status()
    data = response.json()
    rates = []
    for rate_reply in data.get('output', {}).get('rateReplyDetails', []):
        for rated_shipment in rate_reply.get('ratedShipmentDetails', []):
            total = rated_shipment.get('totalNetCharge')
            if total:
                rates.append({
                    'service': rate_reply.get('serviceType'),
                    'transit_days': rate_reply.get('commit', {}).get('dateDetail', {}).get('dayOfWeek'),
                    'total_charge': total,
                    'currency': rated_shipment.get('currency', 'USD')
                })
    return rates

def track_package(tracking_number: str) -> Optional[dict]:
    """Track a FedEx package by tracking number."""
    token = get_access_token()
    payload = {
        "includeDetailedScans": True,
        "trackingInfo": [{"trackingNumberInfo": {"trackingNumber": tracking_number}}]
    }
    response = requests.post(
        f"{BASE_URL}/track/v1/trackingnumbers",
        json=payload,
        headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json", "X-locale": "en_US"}
    )
    response.raise_for_status()
    output = response.json().get('output', {})
    complete_track = output.get('completeTrackResults', [])
    if not complete_track:
        return None
    track_results = complete_track[0].get('trackResults', [])
    if not track_results:
        return None
    result = track_results[0]
    return {
        'tracking_number': tracking_number,
        'status': result.get('latestStatusDetail', {}).get('code'),
        'description': result.get('latestStatusDetail', {}).get('description'),
        'estimated_delivery': result.get('estimatedDeliveryTimeWindow', {}).get('window', {}).get('ends'),
        'actual_delivery': result.get('actualDeliveryTime'),
        'events': result.get('scanEvents', [])
    }

if __name__ == "__main__":
    print("Getting FedEx rates from 10001 to 90210 for 5lb package...")
    rates = get_rates("10001", "90210", 5.0)
    for r in rates:
        print(f"  {r['service']}: ${r['total_charge']} ({r['currency']})")
```

**Expected result:** Running the script prints available FedEx shipping services with rates for a 5lb package between the two zip codes.

### 4. Build a Node.js FedEx Proxy with Express

The Node.js server below implements the same OAuth token caching pattern and exposes clean REST endpoints for your frontend or downstream services. Token caching is critical — FedEx's OAuth endpoint has rate limits, and requesting a new token for every API call will quickly hit those limits in production.

Install dependencies with npm install express axios in the Replit Shell. The server implements endpoints for rates, tracking, and a webhook receiver stub. Bind to 0.0.0.0:3000 for Replit port routing.

For production deployments handling checkout rate requests, deploy with Autoscale to handle traffic spikes during promotional periods when many customers reach checkout simultaneously.

```
const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

const CLIENT_ID = process.env.FEDEX_CLIENT_ID;
const CLIENT_SECRET = process.env.FEDEX_CLIENT_SECRET;
const ACCOUNT_NUMBER = process.env.FEDEX_ACCOUNT_NUMBER || '510087240';
const ENV = process.env.FEDEX_ENV || 'sandbox';
const BASE_URL = ENV === 'production' ? 'https://apis.fedex.com' : 'https://apis-sandbox.fedex.com';

// Token cache
let tokenCache = { token: null, expiresAt: 0 };

async function getAccessToken() {
  if (tokenCache.token && tokenCache.expiresAt > Date.now() + 60000) {
    return tokenCache.token;
  }
  const params = new URLSearchParams();
  params.append('grant_type', 'client_credentials');
  params.append('client_id', CLIENT_ID);
  params.append('client_secret', CLIENT_SECRET);

  const response = await axios.post(`${BASE_URL}/oauth/token`, params, {
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
  });
  tokenCache.token = response.data.access_token;
  tokenCache.expiresAt = Date.now() + (response.data.expires_in || 3600) * 1000;
  return tokenCache.token;
}

// POST /rates — get shipping rates
app.post('/rates', async (req, res) => {
  const { shipperZip, recipientZip, weightLb, length = 12, width = 10, height = 8 } = req.body;
  if (!shipperZip || !recipientZip || !weightLb) {
    return res.status(400).json({ error: 'shipperZip, recipientZip, and weightLb are required' });
  }
  try {
    const token = await getAccessToken();
    const payload = {
      accountNumber: { value: ACCOUNT_NUMBER },
      requestedShipment: {
        shipper: { address: { postalCode: shipperZip, countryCode: 'US' } },
        recipient: { address: { postalCode: recipientZip, countryCode: 'US' } },
        pickupType: 'DROPOFF_AT_FEDEX_LOCATION',
        rateRequestType: ['LIST'],
        requestedPackageLineItems: [{
          weight: { units: 'LB', value: weightLb },
          dimensions: { length, width, height, units: 'IN' }
        }]
      }
    };
    const response = await axios.post(`${BASE_URL}/rate/v1/rates/quotes`, payload, {
      headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', 'X-locale': 'en_US' }
    });
    const rateDetails = response.data?.output?.rateReplyDetails || [];
    const rates = rateDetails.map(r => ({
      service: r.serviceType,
      charge: r.ratedShipmentDetails?.[0]?.totalNetCharge
    }));
    res.json(rates);
  } catch (err) {
    console.error('FedEx rates error:', err.response?.data);
    res.status(500).json({ error: err.message });
  }
});

// GET /track/:number — track a package
app.get('/track/:number', async (req, res) => {
  try {
    const token = await getAccessToken();
    const payload = {
      includeDetailedScans: true,
      trackingInfo: [{ trackingNumberInfo: { trackingNumber: req.params.number } }]
    };
    const response = await axios.post(`${BASE_URL}/track/v1/trackingnumbers`, payload, {
      headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', 'X-locale': 'en_US' }
    });
    const result = response.data?.output?.completeTrackResults?.[0]?.trackResults?.[0];
    if (!result) return res.status(404).json({ error: 'Tracking number not found' });
    res.json({
      status: result.latestStatusDetail?.code,
      description: result.latestStatusDetail?.description,
      estimatedDelivery: result.estimatedDeliveryTimeWindow?.window?.ends,
      events: result.scanEvents?.slice(0, 10)
    });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

app.listen(3000, '0.0.0.0', () => {
  console.log(`FedEx proxy running on port 3000 (${ENV})`)
});
```

**Expected result:** The server starts on port 3000. A POST to /rates with origin and destination zip codes returns available FedEx services with pricing.

## Best practices

- Store FEDEX_CLIENT_ID and FEDEX_CLIENT_SECRET in Replit Secrets (lock icon 🔒) — never hardcode them or expose them to frontend clients.
- Cache the OAuth access token in a module-level variable with its expiry time to avoid requesting a new token on every API call — the token is valid for 3600 seconds.
- Use separate credentials for sandbox and production environments, toggled via a FEDEX_ENV Replit Secret, to prevent accidental production API calls during testing.
- Always validate addresses using the FedEx Address Validation API before creating shipments — invalid addresses cause label creation to fail at the worst possible time.
- Handle the BUSINESS.RULE.VALIDATION.FAILURE error gracefully in your UI — it often means a required field is missing or formatted incorrectly, not a credentials problem.
- For label creation, store the generated PDF (Base64-decoded) in your storage system immediately — FedEx does not store labels and they cannot be regenerated if lost.
- Deploy with Autoscale for checkout rate endpoints handling variable customer traffic; use Reserved VM if your backend processes continuous background label creation.
- Test against the FedEx sandbox environment using test account number 510087240 and sandbox-specific test tracking numbers before requesting production API access.

## Use cases

### Live Shipping Rate Calculator at Checkout

Build a Replit backend that calculates real-time FedEx shipping rates when customers reach your checkout page. Pass package dimensions, weight, origin zip code, and destination zip code to the FedEx Rate API and return available service options — Ground, Express, Overnight — with prices and estimated delivery dates.

Prompt example:

```
Create a Flask server with a POST /shipping-rates endpoint that accepts origin zip, destination zip, weight in pounds, and package dimensions, calls the FedEx Rate API with FEDEX_CLIENT_ID and FEDEX_CLIENT_SECRET from Replit Secrets, and returns available service options with prices.
```

### Automated Shipping Label Generation

After an order is placed, automatically create a FedEx shipping label from your Replit backend. Send the sender and recipient addresses, package weight, and service type to the FedEx Ship API, receive a PDF label back, and store it or email it to your fulfilment team. This eliminates manual label creation for high-volume shippers.

Prompt example:

```
Write a Node.js function that accepts order data (sender address, recipient address, package weight, service type), authenticates with FedEx OAuth, creates a shipment via the FedEx Ship API, and returns the tracking number and a Base64-encoded PDF label.
```

### Customer Delivery Status Notification System

Build a Replit service that tracks FedEx shipments on a schedule and sends proactive SMS or email notifications to customers when their package status changes. Pull tracking events, detect status transitions (in transit → out for delivery → delivered), and trigger SendGrid or Twilio notifications without requiring customers to check manually.

Prompt example:

```
Create a Python script that reads open FedEx tracking numbers from a database, queries the FedEx Tracking API for status updates, compares to the last known status stored in the database, and sends an email notification via SendGrid when the status changes to 'OUT_FOR_DELIVERY' or 'DELIVERED'.
```

## Troubleshooting

### OAuth token request returns 401 with 'AuthenticationFailure'

Cause: The client ID or client secret in Replit Secrets is incorrect, or you are using sandbox credentials against the production endpoint (or vice versa).

Solution: Verify your FEDEX_CLIENT_ID and FEDEX_CLIENT_SECRET in the FedEx Developer Portal under My Projects. Make sure your FEDEX_ENV matches the credentials — sandbox credentials only work against apis-sandbox.fedex.com and production credentials against apis.fedex.com. Re-copy the secret from the portal to avoid transcription errors.

```
# Python: test token endpoint directly
import requests
response = requests.post(
    'https://apis-sandbox.fedex.com/oauth/token',
    data={'grant_type': 'client_credentials', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET},
    headers={'Content-Type': 'application/x-www-form-urlencoded'}
)
print(response.status_code, response.json())
```

### Rate API returns 'BUSINESS.RULE.VALIDATION.FAILURE' for valid addresses

Cause: The FedEx Rate API requires an account number even in sandbox mode for some service types. The sandbox test account number may differ from your real account number.

Solution: Use FedEx's sandbox test account number '510087240' when testing in the sandbox environment. Switch to your real account number in FEDEX_ACCOUNT_NUMBER for production. For international rates, ensure the countryCode fields are ISO 3166-1 alpha-2 codes (e.g., 'US', 'GB', 'DE').

```
# Python: use sandbox test account number
ACCOUNT_NUMBER = os.environ.get('FEDEX_ACCOUNT_NUMBER') or '510087240'  # Sandbox fallback
```

### Tracking returns empty results for a valid FedEx tracking number

Cause: The package may be too new (not yet in the FedEx system), the tracking number format is incorrect, or you are querying sandbox tracking numbers against the production API.

Solution: Verify the tracking number format — FedEx tracking numbers are 12, 15, 20, or 22 digits depending on the service. New labels take 1-2 hours to appear in the tracking system. In sandbox, only specific test tracking numbers return mock data.

```
# Python: print full tracking API response for debugging
response = requests.post(url, json=payload, headers=headers)
print('Status:', response.status_code)
print('Response:', response.json())
```

### Token refresh causes API errors during concurrent requests

Cause: Multiple concurrent requests detect an expired token and simultaneously try to refresh it, causing race conditions with the token cache.

Solution: Add a threading lock around the token refresh logic in Python, or use a queue/mutex in Node.js for concurrent requests. Alternatively, use a token with a generous buffer (refresh when less than 5 minutes remain) to reduce the window for race conditions.

```
import threading
_lock = threading.Lock()

def get_access_token() -> str:
    with _lock:
        if _token_cache.get('token') and _token_cache.get('expires_at', 0) > time.time() + 300:
            return _token_cache['token']
        # ... refresh token ...
```

## Frequently asked questions

### How do I store FedEx API credentials in Replit?

Click the lock icon 🔒 in the Replit sidebar to open Secrets. Add FEDEX_CLIENT_ID and FEDEX_CLIENT_SECRET with values from your FedEx developer project. Add FEDEX_ACCOUNT_NUMBER with your billing account number and FEDEX_ENV set to 'sandbox' or 'production'. Access them in Python with os.environ['FEDEX_CLIENT_ID'] and in Node.js with process.env.FEDEX_CLIENT_ID.

### Does the FedEx API require a paid FedEx account?

Tracking and rate queries are available on a free FedEx developer account with sandbox access. To generate real shipping labels and make production API calls, you need an active FedEx business account. The transition from sandbox to production also requires FedEx's certification process where you demonstrate correct API usage before production credentials are issued.

### How does FedEx OAuth 2.0 work in Replit?

Your Replit backend posts your client ID and client secret to the FedEx OAuth token endpoint to receive a Bearer token valid for one hour. You include this token in the Authorization header for all subsequent API calls. Cache the token with its expiry time and only request a new one when the cached token is about to expire — do not request a new token on every API call.

### Can I track FedEx packages from Replit for free?

Yes. The FedEx Track API is available on the free developer plan and can track any FedEx shipment using a tracking number. You need to implement OAuth token acquisition, but there are no per-query fees for tracking. Register at developer.fedex.com, create a project with the Track API, and you can start tracking packages in the sandbox environment immediately.

### Why does the FedEx rate calculation return different prices than the FedEx website?

The FedEx Rate API returns rates based on your account number, which may have negotiated discounts different from standard retail rates shown on the FedEx website. In sandbox mode, rates are mock values for testing and do not reflect real prices. Use rateRequestType: ['LIST'] for list rates and ['ACCOUNT'] for your account's negotiated rates — both can be requested in the same call.

---

Source: https://www.rapidevelopers.com/replit-integration/fedex-api
© RapidDev — https://www.rapidevelopers.com/replit-integration/fedex-api
