API Integration Examples
Practical code examples for real-world API integration
Related Tools
Why API Integration Matters
Modern applications rarely work alone. They connect to payment gateways, email services, cloud storage, AI APIs, and countless other services. Understanding how to properly integrate APIs is essential for every developer.
This guide provides practical, copy-paste-ready code examples for common API integration scenarios: making requests, handling authentication, processing responses, dealing with errors, and implementing pagination.
1. Basic API Calls with Fetch
Simple GET Request
// Basic GET request
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// Modern async/await version
async function fetchUsers() {
try {
const response = await fetch('https://api.example.com/users');
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to fetch users:', error);
throw error;
}
}Always use async/await for cleaner, more readable code. It makes error handling straightforward with try/catch blocks.
POST Request with JSON Body
// POST request with JSON body
async function createUser(userData) {
try {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to create user:', error);
throw error;
}
}
// Usage
const newUser = await createUser({
name: 'John Doe',
email: '[email protected]',
});2. Authentication Methods
Bearer Token Authentication (Most Common)
Bearer tokens are the most widely used authentication method for modern APIs. They're typically obtained through OAuth 2.0 flows or API login endpoints.
// Bearer token authentication
async function fetchWithBearerToken(url, token) {
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
return response.json();
}
// Usage with stored token
const token = localStorage.getItem('api_token');
const data = await fetchWithBearerToken('https://api.example.com/data', token);
// Example: GitHub API
async function getGitHubUser(username, token) {
const response = await fetch(`https://api.github.com/users/${username}`, {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/vnd.github.v3+json',
},
});
return response.json();
}API Key Authentication
API keys are simple tokens used for service-to-service communication. They can be passed in headers, query parameters, or request body.
// API key in header (recommended)
async function fetchWithApiKey(url, apiKey) {
const response = await fetch(url, {
headers: {
'X-API-Key': apiKey,
'Content-Type': 'application/json',
},
});
return response.json();
}
// API key in query parameter (some APIs require this)
async function fetchWithApiKeyQuery(url, apiKey) {
const response = await fetch(`${url}?api_key=${apiKey}`);
return response.json();
}
// Example: OpenAI API
async function callOpenAI(prompt, apiKey) {
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: prompt }],
}),
});
return response.json();
}HTTP Basic Authentication
Basic Auth encodes username:password in Base64. Use only with HTTPS. Modern APIs often use Basic Auth with API keys instead of actual passwords.
// Basic Auth with Base64 encoding
async function fetchWithBasicAuth(url, username, password) {
// Encode credentials
const credentials = btoa(`${username}:${password}`);
const response = await fetch(url, {
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json',
},
});
return response.json();
}
// Usage
const data = await fetchWithBasicAuth(
'https://api.example.com/data',
'my_username',
'my_api_key' // Often APIs use API key as "password"
);Security Warning
Basic Auth credentials are easily decoded. Always use HTTPS. Consider OAuth 2.0 or Bearer tokens for production applications.
3. Robust Error Handling
Complete Error Handling Pattern
class ApiError extends Error {
constructor(message, status, data) {
super(message);
this.status = status;
this.data = data;
this.name = 'ApiError';
}
}
async function apiRequest(url, options = {}) {
try {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
// Parse response
const data = await response.json();
// Check for HTTP errors
if (!response.ok) {
throw new ApiError(
data.message || `HTTP error! status: ${response.status}`,
response.status,
data
);
}
return data;
} catch (error) {
// Network errors (no connection)
if (error.name === 'TypeError' && error.message.includes('fetch')) {
throw new ApiError('Network error: Unable to connect to API', 0, null);
}
// JSON parse errors
if (error.name === 'SyntaxError') {
throw new ApiError('Invalid JSON response from API', 0, null);
}
// Re-throw ApiError
throw error;
}
}
// Usage with error handling
try {
const data = await apiRequest('https://api.example.com/data');
console.log(data);
} catch (error) {
if (error instanceof ApiError) {
switch (error.status) {
case 401:
console.error('Authentication required');
// Redirect to login
break;
case 403:
console.error('Access forbidden');
break;
case 404:
console.error('Resource not found');
break;
case 500:
console.error('Server error, try again later');
break;
default:
console.error('API error:', error.message);
}
}
}- 400 - Bad Request (invalid input)
- 401 - Unauthorized (need auth)
- 403 - Forbidden (no permission)
- 404 - Not Found
- 429 - Too Many Requests (rate limit)
- 500 - Server Error
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email",
"details": {
"field": "email",
"value": "invalid@"
}
}
}4. Handling Pagination
Paginated API Requests
Most APIs return large datasets in pages. Common patterns include offset/limit, page/per_page, or cursor-based pagination.
// Offset/limit pagination
async function fetchAllPages(baseUrl) {
const limit = 100;
let offset = 0;
let allData = [];
let hasMore = true;
while (hasMore) {
const url = `${baseUrl}?limit=${limit}&offset=${offset}`;
const response = await fetch(url);
const data = await response.json();
allData.push(...data.items);
// Check if more pages exist
hasMore = data.items.length === limit;
offset += limit;
}
return allData;
}
// Cursor-based pagination (recommended for large datasets)
async function fetchWithCursor(baseUrl, cursor = null) {
const url = cursor
? `${baseUrl}?cursor=${cursor}`
: baseUrl;
const response = await fetch(url);
const data = await response.json();
return {
items: data.items,
nextCursor: data.next_cursor,
hasMore: data.has_more,
};
}
// Fetch all pages with cursor
async function fetchAllWithCursor(baseUrl) {
let allData = [];
let cursor = null;
let hasMore = true;
while (hasMore) {
const result = await fetchWithCursor(baseUrl, cursor);
allData.push(...result.items);
cursor = result.nextCursor;
hasMore = result.hasMore;
}
return allData;
}Best practice: Cursor-based pagination is more reliable for large, frequently-changing datasets. Offset pagination can miss or duplicate items if data changes while paginating.
5. Rate Limiting & Retry
Handling Rate Limits with Retry
// Retry with exponential backoff
async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let retries = 0;
while (retries < maxRetries) {
try {
const response = await fetch(url, options);
// Rate limited - wait and retry
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : 1000 * Math.pow(2, retries);
console.log(`Rate limited, waiting ${waitTime}ms...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
retries++;
continue;
}
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
retries++;
if (retries >= maxRetries) {
throw error;
}
// Exponential backoff: 1s, 2s, 4s...
const waitTime = 1000 * Math.pow(2, retries);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
}
}
// Usage
const data = await fetchWithRetry(
'https://api.example.com/data',
{ headers: { 'Authorization': 'Bearer token' } },
3 // max retries
);Exponential backoff prevents hammering the API during outages. Start with 1 second, then 2, 4, 8... Most APIs return Retry-After header.
6. Using Axios (Alternative to Fetch)
Axios Examples
Axios is a popular HTTP client with automatic JSON parsing, better error handling, interceptors, and request cancellation.
import axios from 'axios';
// Create instance with default config
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`,
},
});
// Request interceptor (add auth token dynamically)
api.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor (handle errors globally)
api.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
// Redirect to login
window.location.href = '/login';
}
return Promise.reject(error);
}
);
// Usage
const users = await api.get('/users');
const newUser = await api.post('/users', { name: 'John', email: '[email protected]' });- • Automatic JSON parsing
- • Better error objects
- • Request/response interceptors
- • Request cancellation
- • Timeout support
- • Progress monitoring
- • Built-in (no dependency)
- • Stream support
- • Works in Service Workers
- • Lighter weight
- • Better for edge cases
- • Native browser API
API Integration Best Practices
Always Handle Errors
Never assume API calls succeed. Handle network errors, HTTP errors, and JSON parse errors separately. Show meaningful messages to users.
Use HTTPS Always
Never send credentials or sensitive data over HTTP. Even Basic Auth requires HTTPS to be secure. Most APIs reject non-HTTPS requests anyway.
Store Tokens Securely
Use secure storage for tokens. In browsers, consider sessionStorage over localStorage for sensitive tokens. In apps, use secure storage APIs.
Implement Rate Limit Handling
Respect API rate limits. Implement exponential backoff for retries. Cache responses when possible to reduce API calls.
Validate Responses
Don't trust API responses blindly. Validate structure and types. Use TypeScript interfaces or JSON Schema for validation.