Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with FedEx API

Integrate FedEx's REST API (migrated from SOAP in 2023) into Bolt.new using OAuth 2.0 client credentials. All API calls must be server-side in Next.js API routes — the access token exchange exposes your secret key if done client-side. Bolt.new's WebContainer handles outbound calls for tracking, rate quotes, and label creation. Register at developer.fedex.com to get your Client ID and Secret.

What you'll learn

  • How to register on the FedEx Developer Portal and create a project to get OAuth 2.0 credentials
  • How to implement the client credentials OAuth token exchange in a Next.js API route
  • How to track packages by tracking number using the FedEx Tracking API
  • How to request rate quotes across FedEx services (Ground, Express, Overnight) for a shipment
  • How to cache OAuth tokens to avoid hitting FedEx's rate limits on the token endpoint
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate17 min read30 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

Integrate FedEx's REST API (migrated from SOAP in 2023) into Bolt.new using OAuth 2.0 client credentials. All API calls must be server-side in Next.js API routes — the access token exchange exposes your secret key if done client-side. Bolt.new's WebContainer handles outbound calls for tracking, rate quotes, and label creation. Register at developer.fedex.com to get your Client ID and Secret.

Integrate FedEx Tracking and Shipping Rates into Bolt.new

FedEx completed its migration from the legacy SOAP/XML API to a modern REST API in 2023, making integration significantly more approachable for web developers. The new API at developer.fedex.com covers tracking, rating, ship (label creation), address validation, pickup scheduling, and international documentation — all with standard HTTP and JSON instead of the old XML envelope format.

The most common use case for Bolt.new apps is package tracking: after a customer places an order, display real-time FedEx tracking status without redirecting them to fedex.com. The second most common is rate shopping: before finalizing shipping costs in your checkout, query FedEx for accurate real-time rates based on the shipment's actual dimensions, weight, and destination. Both use cases require server-side API routes since FedEx credentials must never be in client-side code.

OAuth 2.0 adds one layer of complexity compared to simple API key authentication. You first exchange your Client ID and Secret for a bearer token, then include that token in every API call. Tokens expire after one hour. A well-implemented integration caches the token and only re-exchanges when it expires — rather than making a token request before every single API call, which would double your request count and add latency. Bolt.new's WebContainer environment supports this token caching pattern in-memory during development, though cached tokens reset between server restarts.

Integration method

Bolt Chat + API Route

FedEx's REST API uses OAuth 2.0 client credentials flow — your Client ID and Client Secret are exchanged for a short-lived bearer token, which is then included in all subsequent API requests. Both the token exchange and all API calls must happen in Next.js API routes since the credentials would be exposed if used client-side. Bolt.new's WebContainer supports all outbound HTTPS calls to FedEx's API during development. FedEx's developer sandbox environment lets you test tracking and rating without actual shipments.

Prerequisites

  • A FedEx Developer Portal account at developer.fedex.com — sign up with your business FedEx account or create a developer account
  • A project created in the FedEx Developer Portal with API access enabled — provides your Client ID and Client Secret
  • FedEx API access enabled for Tracking, Rating, and Ship APIs in your project settings
  • A Bolt.new account with a new Next.js project open
  • A Netlify account for deploying the production integration with real FedEx production credentials

Step-by-step guide

1

Register on FedEx Developer Portal and get OAuth credentials

FedEx's new REST API requires registration at developer.fedex.com — a separate portal from your regular FedEx shipping account. The registration process creates an API project that is linked to your FedEx account and grants specific API access. To register: go to developer.fedex.com and click 'Get API Access'. Sign in with your FedEx account credentials or create a new developer account. Once logged in, navigate to My Projects → Create a Project. Give your project a name (e.g., 'My App FedEx Integration') and agree to the API terms. In your project settings, add the APIs you need. For the most common integration scenarios, enable: Tracking API (for package status), Rate API (for rate quotes), and Ship API (for label creation). FedEx creates a separate Client ID and Client Secret for each project. Both values are shown in the project detail page — copy them immediately and store them in your password manager. FedEx provides two environments: Sandbox (sandbox.fedex.com APIs, uses test tracking numbers and simulated responses) and Production (apis.fedex.com). Always develop and test against the sandbox first. The sandbox base URL is `https://apis-sandbox.fedex.com` and the production URL is `https://apis.fedex.com`. Use different environment variables for each to make switching easy. Add both credentials to your .env file. These are sensitive credentials that must never be exposed in client-side code or NEXT_PUBLIC_ environment variables. The Client Secret in particular is equivalent to an API password.

Bolt.new Prompt

Set up FedEx API authentication in my Next.js project. Create a .env file with FEDEX_CLIENT_ID=your-client-id, FEDEX_CLIENT_SECRET=your-client-secret, and FEDEX_API_BASE_URL=https://apis-sandbox.fedex.com (sandbox for development). Create a lib/fedex.ts file that: (1) exports a getFedExToken function that POSTs to /oauth/token with grant_type=client_credentials and the credentials, caches the token in a module-level variable with expiry tracking, and returns a valid token. (2) exports a fedexFetch function that calls getFedExToken, then makes an authenticated request with the Bearer token. Add TypeScript types.

Paste this in Bolt.new chat

lib/fedex.ts
1// lib/fedex.ts
2interface TokenCache {
3 token: string;
4 expiresAt: number;
5}
6
7let tokenCache: TokenCache | null = null;
8
9async function getFedExToken(): Promise<string> {
10 const now = Date.now();
11 // Return cached token if still valid (with 60s buffer)
12 if (tokenCache && tokenCache.expiresAt > now + 60_000) {
13 return tokenCache.token;
14 }
15
16 const clientId = process.env.FEDEX_CLIENT_ID;
17 const clientSecret = process.env.FEDEX_CLIENT_SECRET;
18 const baseUrl = process.env.FEDEX_API_BASE_URL ?? 'https://apis-sandbox.fedex.com';
19
20 if (!clientId || !clientSecret) {
21 throw new Error('FEDEX_CLIENT_ID and FEDEX_CLIENT_SECRET must be set in .env');
22 }
23
24 const response = await fetch(`${baseUrl}/oauth/token`, {
25 method: 'POST',
26 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
27 body: new URLSearchParams({
28 grant_type: 'client_credentials',
29 client_id: clientId,
30 client_secret: clientSecret,
31 }),
32 });
33
34 if (!response.ok) {
35 const err = await response.text();
36 throw new Error(`FedEx token exchange failed: ${err}`);
37 }
38
39 const data = await response.json() as { access_token: string; expires_in: number };
40 tokenCache = {
41 token: data.access_token,
42 expiresAt: now + data.expires_in * 1000,
43 };
44
45 return data.access_token;
46}
47
48export async function fedexFetch<T = unknown>(
49 endpoint: string,
50 init: RequestInit = {}
51): Promise<T> {
52 const token = await getFedExToken();
53 const baseUrl = process.env.FEDEX_API_BASE_URL ?? 'https://apis-sandbox.fedex.com';
54 const url = `${baseUrl}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
55
56 const response = await fetch(url, {
57 ...init,
58 headers: {
59 Authorization: `Bearer ${token}`,
60 'Content-Type': 'application/json',
61 'X-Customer-Transaction-Id': `bolt-${Date.now()}`,
62 'X-locale': 'en_US',
63 ...init.headers,
64 },
65 });
66
67 if (!response.ok) {
68 const errorData = await response.text();
69 throw new Error(`FedEx API ${response.status} at ${endpoint}: ${errorData}`);
70 }
71
72 return response.json() as Promise<T>;
73}

Pro tip: FedEx tokens expire after one hour. The module-level token cache in lib/fedex.ts persists across multiple API calls within the same server process, avoiding redundant token exchanges. In Bolt.new's development environment, the cache resets when the dev server restarts — this is expected behavior.

Expected result: A configured FedEx authentication helper with token caching in lib/fedex.ts, and credentials in .env pointing at the sandbox environment.

2

Build the package tracking API route

The FedEx Tracking API (`POST /track/v1/trackingnumbers`) returns comprehensive shipment status including the latest scan event, all historical events, estimated delivery date and time, delivery location details, and exception information for delayed or problem shipments. The request body requires a `trackingInfo` array with objects containing `trackingNumberInfo.trackingNumber`. You can track up to 30 shipments in a single API call, which is useful for building an order tracking page that shows multiple packages. The response structure is nested: `output.completeTrackResults[0].trackResults[0]` contains the main tracking data. The `latestStatusDetail` object has `description` (human-readable status like 'On FedEx vehicle for delivery') and `code` (machine-readable status code like 'OD'). The `estimatedDeliveryTimeWindow` object has the expected delivery date range. The `scanEvents` array contains all tracking events in reverse chronological order — the first element is the most recent. Each event has `date`, `time`, `eventType`, `eventDescription`, and `scanLocation` (city, state). Displaying the 5 most recent events gives users a clear view of their package's journey. For the sandbox, FedEx provides test tracking numbers in their documentation: `128667043726` for a delivered package, `49044304137821` for in-transit. Always test against sandbox tracking numbers before switching to production credentials. Important: the tracking API call works in Bolt's WebContainer — it is a straightforward outbound POST request and there is no WebContainer limitation for this use case.

Bolt.new Prompt

Create a FedEx tracking API route at app/api/fedex/track/route.ts. Accept POST with trackingNumber string. Use fedexFetch from lib/fedex.ts to POST to /track/v1/trackingnumbers with the correct request body format. Extract the status description, estimatedDelivery, and last 5 scan events from the response. Return { status, description, estimatedDelivery, events: [{date, time, location, description}] }. Handle the case where the tracking number is not found. Create a React TrackingWidget component that shows a progress timeline of events with the current status prominently displayed.

Paste this in Bolt.new chat

app/api/fedex/track/route.ts
1// app/api/fedex/track/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { fedexFetch } from '@/lib/fedex';
4
5export async function POST(request: NextRequest) {
6 const { trackingNumber } = await request.json();
7
8 if (!trackingNumber?.trim()) {
9 return NextResponse.json({ error: 'trackingNumber is required' }, { status: 400 });
10 }
11
12 const body = {
13 includeDetailedScans: true,
14 trackingInfo: [{
15 trackingNumberInfo: { trackingNumber: trackingNumber.trim() },
16 }],
17 };
18
19 const data = await fedexFetch<Record<string, unknown>>('/track/v1/trackingnumbers', {
20 method: 'POST',
21 body: JSON.stringify(body),
22 });
23
24 const trackResult = (
25 data as {
26 output?: {
27 completeTrackResults?: Array<{
28 trackResults?: Array<{
29 latestStatusDetail?: { description: string; code: string };
30 estimatedDeliveryTimeWindow?: { window?: { ends?: string } };
31 scanEvents?: Array<{
32 date: string;
33 time: string;
34 eventType: string;
35 eventDescription: string;
36 scanLocation?: { city: string; stateOrProvinceCode: string };
37 }>;
38 }>;
39 }>;
40 };
41 }
42 )?.output?.completeTrackResults?.[0]?.trackResults?.[0];
43
44 if (!trackResult) {
45 return NextResponse.json({ error: 'Tracking number not found' }, { status: 404 });
46 }
47
48 const events = (trackResult.scanEvents ?? []).slice(0, 5).map(e => ({
49 date: e.date,
50 time: e.time,
51 description: e.eventDescription,
52 location: e.scanLocation
53 ? `${e.scanLocation.city}, ${e.scanLocation.stateOrProvinceCode}`
54 : 'Unknown location',
55 }));
56
57 return NextResponse.json({
58 status: trackResult.latestStatusDetail?.code,
59 description: trackResult.latestStatusDetail?.description,
60 estimatedDelivery: trackResult.estimatedDeliveryTimeWindow?.window?.ends,
61 events,
62 });
63}

Pro tip: FedEx sandbox tracking numbers can be found in the FedEx Developer Portal documentation under the Tracking API guide. Use number 128667043726 to test a 'Delivered' response and 49044304137821 for an in-transit shipment with scan events.

Expected result: A tracking API route that returns current status, estimated delivery, and recent scan events for any FedEx tracking number.

3

Implement the FedEx Rate API for shipment cost estimation

The FedEx Rate API (`POST /rate/v1/rates/quotes`) returns price quotes for all available FedEx services between two addresses. This is the 'rate shopping' use case — showing customers or your fulfillment team the cost and estimated delivery time for Ground, Express, 2Day, and Overnight services before committing to a label. The rate request body is detailed but follows a clear structure. You need: shipper address (including postal code and country), recipient address (including postal code, country, and residential flag), package details (weight with units, and optionally dimensions), and optionally a preferred service type. Setting `rateRequestType: ['LIST', 'PREFERRED']` returns list rates (published rates without account discounts) and any negotiated rates your FedEx account qualifies for. The response includes a `rateReplyDetails` array with one entry per service type available for the route. Each entry has `serviceType` (e.g., FEDEX_GROUND, PRIORITY_OVERNIGHT), `serviceName` (human readable), `commit` (estimated delivery date and day), and `ratedShipmentDetails` array containing the shipment charges. The `totalNetCharge` within the rated shipment details is the total cost including all surcharges. FedEx applies various surcharges that increase the base rate: Residential Delivery Surcharge (for deliveries to homes instead of businesses), Fuel Surcharge (percentage applied to all shipments), Oversized surcharge (for packages over certain dimensions), and Extended Delivery Area charges for remote ZIP codes. Your rate response will show these broken out, but `totalNetCharge` captures everything. Sorting results by delivery date rather than price helps users make informed decisions — showing 'Ground: $8.50 (5 days)' versus 'Priority Overnight: $45.00 (1 day)' lets users pick based on urgency and budget.

Bolt.new Prompt

Create a FedEx rate quote API route at app/api/fedex/rates/route.ts. Accept POST with: fromZip (string), toZip (string), weightLbs (number), toResidential (boolean, default false), and optional dimensions (length, width, height in inches). Use fedexFetch to POST to /rate/v1/rates/quotes with shipper address (use process.env.FEDEX_SHIPPER_ZIP for the from ZIP or fall back to the provided fromZip), recipient address, and package details. Return an array of { serviceType, serviceName, deliveryDate, totalCharge, currency } sorted by totalCharge ascending. Handle errors gracefully.

Paste this in Bolt.new chat

app/api/fedex/rates/route.ts
1// app/api/fedex/rates/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { fedexFetch } from '@/lib/fedex';
4
5export async function POST(request: NextRequest) {
6 const { fromZip, toZip, weightLbs, toResidential = false, dimensions } =
7 await request.json();
8
9 if (!fromZip || !toZip || !weightLbs) {
10 return NextResponse.json(
11 { error: 'fromZip, toZip, and weightLbs are required' },
12 { status: 400 }
13 );
14 }
15
16 const requestBody = {
17 accountNumber: { value: process.env.FEDEX_ACCOUNT_NUMBER ?? '' },
18 rateRequestType: ['LIST'],
19 requestedShipment: {
20 shipper: {
21 address: {
22 postalCode: fromZip,
23 countryCode: 'US',
24 residential: false,
25 },
26 },
27 recipient: {
28 address: {
29 postalCode: toZip,
30 countryCode: 'US',
31 residential: toResidential,
32 },
33 },
34 pickupType: 'USE_SCHEDULED_PICKUP',
35 rateRequestType: ['LIST'],
36 requestedPackageLineItems: [{
37 weight: { value: weightLbs, units: 'LB' },
38 ...(dimensions ? {
39 dimensions: {
40 length: dimensions.length,
41 width: dimensions.width,
42 height: dimensions.height,
43 units: 'IN',
44 },
45 } : {}),
46 }],
47 },
48 };
49
50 const data = await fedexFetch<Record<string, unknown>>('/rate/v1/rates/quotes', {
51 method: 'POST',
52 body: JSON.stringify(requestBody),
53 });
54
55 const details = (data as { output?: { rateReplyDetails?: unknown[] } })
56 ?.output?.rateReplyDetails ?? [];
57
58 const rates = (details as Array<Record<string, unknown>>).map(d => {
59 const ratedDetails = (d.ratedShipmentDetails as Array<Record<string, unknown>>)?.[0];
60 const charges = ratedDetails?.shipmentRateDetail as Record<string, unknown>;
61 const totalCharge = (charges?.totalNetCharge as Record<string, unknown>);
62 return {
63 serviceType: d.serviceType,
64 serviceName: d.serviceName,
65 deliveryDate: (d.commit as Record<string, unknown>)?.dateDetail,
66 totalCharge: Number(totalCharge?.amount ?? 0),
67 currency: String(totalCharge?.currency ?? 'USD'),
68 };
69 }).sort((a, b) => a.totalCharge - b.totalCharge);
70
71 return NextResponse.json({ rates });
72}

Pro tip: Add your FedEx account number as FEDEX_ACCOUNT_NUMBER in .env. The account number is required for rate requests even in sandbox mode — use your test account number from the FedEx Developer Portal. Without it, rate requests return errors about missing account information.

Expected result: A rate quote API route that returns FedEx service options with pricing and estimated delivery dates, sorted by total cost for easy comparison.

4

Deploy to Netlify and switch to production FedEx credentials

All FedEx API interactions in this guide — token exchange, tracking, and rate queries — are outbound HTTP requests that work perfectly in Bolt.new's WebContainer during development. The WebContainer limitation (no incoming connections) does not affect any of the primary FedEx API use cases described here. FedEx does not offer webhook-style push notifications for most events — shipping events are pull-based (you call the Tracking API to get current status). However, if you implement FedEx Freight features or FedEx Signature Required notifications, those services may require a callback URL. For standard tracking and rating, no webhook configuration is needed. The main deployment task is switching from the FedEx sandbox environment to production. In sandbox, you use test tracking numbers and simulated rate data. In production, you interact with real FedEx shipment data and your actual account rates. To deploy: click Deploy in Bolt.new and connect to Netlify. After deployment, go to Netlify Dashboard → Site Configuration → Environment Variables. Add the production credentials: `FEDEX_CLIENT_ID` and `FEDEX_CLIENT_SECRET` (from your FedEx Developer Portal production project), and change `FEDEX_API_BASE_URL` from `https://apis-sandbox.fedex.com` to `https://apis.fedex.com`. Also add `FEDEX_ACCOUNT_NUMBER` if you use the rate API. To create a production project in FedEx Developer Portal: go to My Projects → Create New Project, select 'Production', and request approval for the APIs you need. FedEx production access requires a brief approval process — submit your project for review and FedEx typically approves within 1-3 business days. Sandbox access is instant. Update your FEDEX_API_BASE_URL environment variable to toggle between sandbox and production without changing any code.

Bolt.new Prompt

Prepare my FedEx integration for Netlify deployment. Create a netlify.toml with Next.js plugin configuration. Add an environment variable validation check at the top of lib/fedex.ts that logs a warning if FEDEX_API_BASE_URL is not set. Create a test page at app/test-fedex/page.tsx (development only — check NODE_ENV) with a form to track a package by number and display the result, so I can verify the FedEx connection works after deployment by visiting /test-fedex.

Paste this in Bolt.new chat

netlify.toml
1// netlify.toml
2[[plugins]]
3package = "@netlify/plugin-nextjs"
4
5[build]
6 command = "npm run build"
7 publish = ".next"
8
9[build.environment]
10 NEXT_TELEMETRY_DISABLED = "1"
11
12# Environment variables needed (set in Netlify dashboard):
13# FEDEX_CLIENT_ID from FedEx Developer Portal (production project)
14# FEDEX_CLIENT_SECRET from FedEx Developer Portal (production project)
15# FEDEX_API_BASE_URL https://apis.fedex.com (production) or https://apis-sandbox.fedex.com (sandbox)
16# FEDEX_ACCOUNT_NUMBER your FedEx account number (required for rating API)

Pro tip: Keep two separate projects in the FedEx Developer Portal — one named 'My App Sandbox' and one 'My App Production'. Use different environment variable sets in Netlify (you can use deploy contexts) to point staging deployments at sandbox and production deployments at the production FedEx API. This prevents test tracking calls from appearing in your production FedEx account logs.

Expected result: A deployed Netlify app using production FedEx credentials, with sandbox credentials still used in local development via .env.

Common use cases

Package Tracking Status Widget

After a customer receives their tracking number, display real-time FedEx tracking status directly in your app — showing the estimated delivery date, current scan location, and delivery progress — without sending them to fedex.com.

Bolt.new Prompt

Build a FedEx package tracking feature. Create an API route at app/api/fedex/track/route.ts that accepts POST with a trackingNumber. Exchange FedEx client credentials (FEDEX_CLIENT_ID and FEDEX_CLIENT_SECRET from process.env) for an OAuth token via POST to https://apis-sandbox.fedex.com/oauth/token with grant_type=client_credentials. Then call the FedEx Tracking API at /track/v1/trackingnumbers with the token. Return status, description, estimated delivery date, and the most recent scan event. Create a React TrackingWidget component with a tracking number input and status display.

Copy this prompt to try it in Bolt.new

Checkout Shipping Rate Calculator

Before finalizing shipping options at checkout, query FedEx for real-time rate quotes based on the customer's destination ZIP code and your package dimensions. Display Ground, Express, and Overnight options with accurate prices and estimated delivery dates.

Bolt.new Prompt

Create a FedEx rate calculator for my checkout. Build an API route at app/api/fedex/rates/route.ts that accepts POST with fromZip, toZip, weightLbs, and optional dimensions (length, width, height in inches). Get an OAuth token from FedEx sandbox, then call the Rate API at /rate/v1/rates/quotes. Return a list of service options with serviceName, deliveryDate, and totalCharge. Sort by totalCharge ascending. Build a React ShippingOptions component that shows the rate options as selectable cards.

Copy this prompt to try it in Bolt.new

Shipping Label Generator

Allow your fulfillment team to create FedEx shipping labels directly from your order management dashboard. Accept order details, get a rate quote for carrier selection, then generate a label and return the tracking number and printable label PDF.

Bolt.new Prompt

Build a shipping label creation flow. Create an API route at app/api/fedex/create-shipment/route.ts that accepts POST with shipper address, recipient address, package weight and dimensions, and serviceType (e.g., FEDEX_GROUND). Call the FedEx Ship API at /ship/v1/shipments with an OAuth token. Return the trackingNumber and label base64 data. Add a note that label creation uses real FedEx production credentials and charges the account — test only in sandbox mode. Create a form component for entering shipment details.

Copy this prompt to try it in Bolt.new

Troubleshooting

OAuth token exchange returns 401 or 'invalid_client' error

Cause: The Client ID or Client Secret is incorrect, or you are mixing sandbox and production credentials with the wrong API base URL. Sandbox credentials only work with apis-sandbox.fedex.com and production credentials only work with apis.fedex.com.

Solution: Verify the credentials match the environment: for sandbox development, use apis-sandbox.fedex.com with sandbox project credentials. Check that FEDEX_CLIENT_ID is the client ID (not the project name) and FEDEX_CLIENT_SECRET is the full secret string. Regenerate credentials in the FedEx Developer Portal if you suspect they were entered incorrectly.

Rate API returns 'Account not found' or 400 error on rate requests

Cause: The FedEx Rate API requires a valid account number in the request body's accountNumber field. If FEDEX_ACCOUNT_NUMBER is not set or is incorrect, the rate request is rejected. This is different from the tracking API which does not require an account number.

Solution: Add FEDEX_ACCOUNT_NUMBER to your .env file. Your FedEx account number is a 9-digit number found in your FedEx account profile or in the FedEx Developer Portal under your project settings. In sandbox mode, use the test account number provided in the FedEx Developer Portal documentation.

typescript
1// In .env — add your FedEx account number
2// FEDEX_ACCOUNT_NUMBER=123456789

FedEx API calls work in Bolt.new development but fail after deploying to Netlify

Cause: The most common cause is that environment variables were not added to Netlify's environment variable settings after deployment. .env files are local-only and never deployed. A secondary cause is that FEDEX_API_BASE_URL is still pointing at the sandbox URL when production credentials are being used.

Solution: Go to Netlify Dashboard → Site Configuration → Environment Variables and verify that FEDEX_CLIENT_ID, FEDEX_CLIENT_SECRET, FEDEX_API_BASE_URL, and FEDEX_ACCOUNT_NUMBER are all set. If switching to production, ensure FEDEX_API_BASE_URL is https://apis.fedex.com (not the sandbox URL). Trigger a redeploy after adding variables.

Tracking API returns empty scan events for a tracking number that shows history on fedex.com

Cause: Sandbox mode only returns simulated data for the specific test tracking numbers provided in FedEx documentation. Real tracking numbers do not work in sandbox mode — only in production. If testing with real tracking numbers, ensure FEDEX_API_BASE_URL is set to the production URL.

Solution: For sandbox testing, use the test tracking numbers from FedEx documentation (e.g., 128667043726 for delivered status). For real shipment data, update FEDEX_API_BASE_URL to https://apis.fedex.com and use production credentials. The sandbox environment intentionally cannot access real shipment data.

Best practices

  • Cache OAuth tokens in a module-level variable with expiry tracking — FedEx tokens last one hour and making a token exchange before every API call doubles your request count unnecessarily
  • Keep separate FedEx Developer Portal projects for sandbox and production environments, with corresponding separate environment variable sets
  • Store FEDEX_CLIENT_ID, FEDEX_CLIENT_SECRET, and FEDEX_ACCOUNT_NUMBER as server-side environment variables only — never use NEXT_PUBLIC_ prefix for any FedEx credentials
  • Always develop against the sandbox (apis-sandbox.fedex.com) to avoid creating real shipments or consuming production API quota during development
  • Include the X-Customer-Transaction-Id header in all API requests with a unique identifier — this makes it much easier to trace specific requests in FedEx support tickets
  • Parse and surface FedEx error codes to users rather than generic 500 errors — FedEx returns detailed error messages that explain exactly what is wrong with a request
  • For rate requests, always set the residential flag correctly — residential deliveries cost more than commercial, and an incorrect flag can cause checkout price discrepancies
  • Validate tracking numbers format before sending to the API — FedEx tracking numbers are 12-22 digits, and validating client-side catches obvious typos before hitting the API

Alternatives

Frequently asked questions

Can I use the FedEx API in Bolt.new without deploying?

Yes. All FedEx API calls — OAuth token exchange, tracking, rate quotes, and label creation — are outbound HTTP requests that work perfectly in Bolt.new's WebContainer during development. The WebContainer limitation only affects incoming connections (like webhook callbacks). FedEx does not push data to your app for core tracking and rating use cases, so no deployment is required for development and testing.

Do I need a FedEx account to use the FedEx API?

Yes, you need a FedEx account to register on the FedEx Developer Portal. For sandbox/testing, you can use a developer account without an active shipping account. For production use (real tracking numbers and live rate quotes), you need a FedEx business account. FedEx accounts are free to create at fedex.com — you only pay when you actually ship packages.

How do FedEx OAuth tokens work and how long do they last?

FedEx uses OAuth 2.0 client credentials flow. You POST your Client ID and Secret to /oauth/token and receive an access token that expires in 3600 seconds (one hour). You include this token as a Bearer Authorization header in every subsequent API call. The lib/fedex.ts helper in this guide caches the token in memory and automatically exchanges for a new one when it is close to expiry, so your API routes always have a valid token without manual management.

What is the difference between FedEx sandbox and production APIs?

The sandbox (apis-sandbox.fedex.com) returns simulated data using a set of test tracking numbers and generates fake rate quotes. No real shipments are created and no charges are incurred. Production (apis.fedex.com) accesses real FedEx shipment data for your account, returns actual carrier rates, and creates real labels when you call the Ship API. Always develop against sandbox, and switch to production credentials only after testing your integration thoroughly.

Why does the FedEx Rate API need an account number when Tracking does not?

The tracking API is publicly accessible with just an OAuth token — FedEx provides tracking data openly. Rate quotes, however, are account-specific: your negotiated rates, available services, and surcharges depend on your specific FedEx account and its contractual terms. The account number tells FedEx which rate tier and service agreements to apply when generating your quote.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.