TypeScript types for PriceFetch API responses, a reusable client class with error handling and retries, and examples using both fetch and axios.
Start by defining types that match the PriceFetch API response format. These types give you autocomplete, catch errors at compile time, and serve as documentation for the API's response structure.
The API always returns a consistent envelope: `success` boolean, `data` on success, `error` on failure, plus metadata like `credits_remaining` and `request_id`.
// types/pricefetch.ts
interface PriceData {
url: string;
price: number;
currency: string;
in_stock: boolean;
retailer: string;
timestamp: string;
}
interface PriceFetchError {
code: string;
message: string;
}
interface PriceFetchSuccess {
success: true;
data: PriceData;
credits_remaining: number;
request_id: string;
}
interface PriceFetchFailure {
success: false;
error: PriceFetchError;
request_id: string;
}
type PriceFetchResponse = PriceFetchSuccess | PriceFetchFailure;
// Error codes you'll encounter
type PriceFetchErrorCode =
| "UNSUPPORTED_RETAILER"
| "PRICE_NOT_FOUND"
| "PAGE_LOAD_FAILED"
| "SCRAPE_FAILED"
| "INVALID_API_KEY"
| "RATE_LIMITED"
| "INSUFFICIENT_CREDITS";Try it yourself — 500 free API credits, no credit card required.
Start FreeNode.js 18+ has built-in `fetch`, so you don't need any HTTP library. Here's a minimal client function that handles the API call and type-narrows the response.
The key pattern: check `response.success` to narrow the type. TypeScript knows that inside the `if (response.success)` block, `response.data` exists. Outside it, `response.error` exists.
// lib/pricefetch.ts
const API_KEY = process.env.PRICEFETCH_API_KEY!;
const BASE_URL = "https://api.pricefetch.dev/v1";
export async function getPrice(productUrl: string): Promise<PriceData> {
const url = new URL(`${BASE_URL}/price`);
url.searchParams.set("url", productUrl);
const res = await fetch(url.toString(), {
headers: { "X-API-Key": API_KEY },
signal: AbortSignal.timeout(15_000),
});
const json: PriceFetchResponse = await res.json();
if (!json.success) {
throw new PriceFetchApiError(json.error.code, json.error.message, json.request_id);
}
return json.data;
}
// Custom error class for PriceFetch errors
export class PriceFetchApiError extends Error {
constructor(
public readonly code: string,
message: string,
public readonly requestId: string,
) {
super(message);
this.name = "PriceFetchApiError";
}
get isRetryable(): boolean {
return ["SCRAPE_FAILED", "PAGE_LOAD_FAILED"].includes(this.code);
}
}For production use, wrap the API in a class that handles retries, timeout configuration, and logging. This client retries transient errors with exponential backoff and provides a clean interface for the rest of your application.
// lib/PriceFetchClient.ts
interface ClientOptions {
apiKey: string;
baseUrl?: string;
timeout?: number;
maxRetries?: number;
}
export class PriceFetchClient {
private apiKey: string;
private baseUrl: string;
private timeout: number;
private maxRetries: number;
constructor(options: ClientOptions) {
this.apiKey = options.apiKey;
this.baseUrl = options.baseUrl ?? "https://api.pricefetch.dev/v1";
this.timeout = options.timeout ?? 15_000;
this.maxRetries = options.maxRetries ?? 2;
}
async getPrice(productUrl: string): Promise<PriceData> {
let lastError: Error | null = null;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await this.fetchPrice(productUrl);
} catch (err) {
lastError = err as Error;
if (err instanceof PriceFetchApiError && !err.isRetryable) {
throw err; // Don't retry client errors
}
if (attempt < this.maxRetries) {
await this.sleep(Math.pow(2, attempt) * 1000);
}
}
}
throw lastError;
}
async getPrices(urls: string[], concurrency = 5): Promise<Map<string, PriceData | Error>> {
const results = new Map<string, PriceData | Error>();
const queue = [...urls];
const worker = async () => {
while (queue.length > 0) {
const url = queue.shift()!;
try {
results.set(url, await this.getPrice(url));
} catch (err) {
results.set(url, err as Error);
}
}
};
await Promise.all(Array.from({ length: concurrency }, () => worker()));
return results;
}
private async fetchPrice(productUrl: string): Promise<PriceData> {
const url = new URL(`${this.baseUrl}/price`);
url.searchParams.set("url", productUrl);
const res = await fetch(url.toString(), {
headers: { "X-API-Key": this.apiKey },
signal: AbortSignal.timeout(this.timeout),
});
const json: PriceFetchResponse = await res.json();
if (!json.success) {
throw new PriceFetchApiError(json.error.code, json.error.message, json.request_id);
}
return json.data;
}
private sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}Here's how to use the client in different scenarios: single price fetch, bulk checking, and error handling. The client class handles retries internally, so your application code stays clean.
// Single price check
const client = new PriceFetchClient({
apiKey: process.env.PRICEFETCH_API_KEY!,
});
const price = await client.getPrice("https://www.amazon.com/dp/B0CHX3QBCH");
console.log(`${price.currency} ${price.price}`); // USD 298.00
// Bulk price check
const urls = [
"https://www.amazon.com/dp/B0CHX3QBCH",
"https://www.amazon.com/dp/B0BSHF7WHW",
"https://www.walmart.com/ip/12345",
];
const results = await client.getPrices(urls, 3);
for (const [url, result] of results) {
if (result instanceof Error) {
console.error(`Failed: ${url} - ${result.message}`);
} else {
console.log(`${result.retailer}: ${result.currency} ${result.price}`);
}
}
// Error handling
try {
const data = await client.getPrice("https://unsupported-site.com/product");
} catch (err) {
if (err instanceof PriceFetchApiError) {
switch (err.code) {
case "UNSUPPORTED_RETAILER":
console.log("This retailer isn't supported yet");
break;
case "RATE_LIMITED":
console.log("Slow down — too many requests");
break;
default:
console.log(`API error: ${err.message}`);
}
}
}If you're already using axios in your project, here's the equivalent setup. The only meaningful difference is how timeouts and headers are configured — the PriceFetch API works the same either way.
import axios, { AxiosInstance } from "axios";
const api: AxiosInstance = axios.create({
baseURL: "https://api.pricefetch.dev/v1",
timeout: 15_000,
headers: { "X-API-Key": process.env.PRICEFETCH_API_KEY! },
});
async function getPrice(productUrl: string): Promise<PriceData> {
const { data: json } = await api.get<PriceFetchResponse>("/price", {
params: { url: productUrl },
});
if (!json.success) {
throw new PriceFetchApiError(json.error.code, json.error.message, json.request_id);
}
return json.data;
}Sign up in 30 seconds. No credit card required. One credit per successful API call.