Skip to content

Instantly share code, notes, and snippets.

@vapvarun
Created April 6, 2026 06:47
Show Gist options
  • Select an option

  • Save vapvarun/e0c4cd4c507eaed9a46cc0e5c9bd813d to your computer and use it in GitHub Desktop.

Select an option

Save vapvarun/e0c4cd4c507eaed9a46cc0e5c9bd813d to your computer and use it in GitHub Desktop.
How to Use the EDD REST API to Build Custom Integrations and Mobile Apps (eddsellservices.com)
<?php
/**
* EDD REST API - API Key Authentication Example
*
* The EDD REST API uses a public key + secret token pair.
* Generate these under Users > Your Profile > EDD API Keys.
*
* Authentication via query string:
* https://yourstore.com/edd-api/v2/products?key=PUBLIC_KEY&token=TOKEN
*
* Authentication via request header (preferred for mobile apps):
*/
// PHP cURL example - query string auth
$response = wp_remote_get( add_query_arg( [
'key' => 'your_public_key',
'token' => 'your_secret_token',
], 'https://yourstore.com/edd-api/v2/products' ) );
$products = json_decode( wp_remote_retrieve_body( $response ), true );
// PHP cURL example - Basic Auth header (encode key:token as base64)
$credentials = base64_encode( 'your_public_key:your_secret_token' );
$response = wp_remote_get( 'https://yourstore.com/edd-api/v2/products', [
'headers' => [
'Authorization' => 'Basic ' . $credentials,
],
] );
/**
* EDD REST API - Fetch Products (JavaScript / React Native)
*
* Works in browser, Node.js, and React Native.
* Uses query-string auth since mobile apps can't store server-side secrets safely.
* Store credentials in a backend proxy for production apps.
*/
const EDD_BASE_URL = 'https://yourstore.com/edd-api/v2';
const EDD_KEY = 'your_public_key';
const EDD_TOKEN = 'your_secret_token';
async function fetchProducts( page = 1, perPage = 10 ) {
const url = new URL( `${EDD_BASE_URL}/products` );
url.searchParams.set( 'key', EDD_KEY );
url.searchParams.set( 'token', EDD_TOKEN );
url.searchParams.set( 'page', page );
url.searchParams.set( 'number', perPage );
const response = await fetch( url.toString() );
if ( ! response.ok ) {
throw new Error( `API error: ${response.status}` );
}
return response.json();
}
async function fetchSingleProduct( productId ) {
const url = new URL( `${EDD_BASE_URL}/products/${productId}` );
url.searchParams.set( 'key', EDD_KEY );
url.searchParams.set( 'token', EDD_TOKEN );
const response = await fetch( url.toString() );
return response.json();
}
// Usage
fetchProducts( 1, 20 ).then( data => {
console.log( `Total products: ${data.products.length}` );
data.products.forEach( product => {
console.log( `${product.info.title} - $${product.pricing.amount}` );
});
});
<?php
/**
* EDD REST API - Register a Custom Endpoint
*
* Adds: GET /edd-api/v2/custom/featured-products
*
* Place this in your theme's functions.php or a custom plugin.
*/
add_action( 'edd_api_v2_query_modes', 'eddapi_register_featured_mode' );
function eddapi_register_featured_mode( $query_modes ) {
$query_modes['featured-products'] = [
'title' => 'Featured Products',
'function' => 'eddapi_get_featured_products',
];
return $query_modes;
}
function eddapi_get_featured_products( $data ) {
// Authenticate the request
if ( ! edd_api_is_valid_request( $data ) ) {
return [
'error' => [
'error' => 'Invalid API key or token.',
'code' => 401,
],
];
}
// Pull products with a custom meta flag
$featured_ids = get_posts( [
'post_type' => 'download',
'post_status' => 'publish',
'posts_per_page' => 6,
'meta_key' => '_edd_featured',
'meta_value' => '1',
'fields' => 'ids',
] );
$products = [];
foreach ( $featured_ids as $id ) {
$products[] = [
'id' => $id,
'title' => get_the_title( $id ),
'price' => edd_get_download_price( $id ),
'url' => edd_get_download_file_url( null, null, $id ),
'thumb' => get_the_post_thumbnail_url( $id, 'medium' ),
];
}
return [
'featured_products' => $products,
'count' => count( $products ),
];
}
<?php
/**
* EDD REST API - Rate Limiting with Transients
*
* Limits each API key to 60 requests per minute.
* Place in functions.php or a custom plugin.
*/
add_filter( 'edd_api_output_before', 'eddapi_enforce_rate_limit', 10, 3 );
function eddapi_enforce_rate_limit( $output, $query_mode, $data ) {
if ( empty( $data['key'] ) ) {
return $output;
}
$api_key = sanitize_text_field( $data['key'] );
$transient_key = 'edd_api_rate_' . md5( $api_key );
$window = 60; // seconds
$max_requests = 60; // per window
$current = get_transient( $transient_key );
if ( false === $current ) {
set_transient( $transient_key, 1, $window );
} elseif ( (int) $current >= $max_requests ) {
status_header( 429 );
wp_send_json( [
'error' => [
'error' => 'Rate limit exceeded. Try again in a minute.',
'code' => 429,
],
] );
exit;
} else {
set_transient( $transient_key, (int) $current + 1, $window );
}
return $output;
}
<?php
/**
* EDD REST API - Zapier Webhook on Completed Payment
*
* Sends purchase data to a Zapier webhook whenever a payment completes.
* Replace ZAPIER_WEBHOOK_URL with your actual Zap trigger URL.
*/
add_action( 'edd_complete_purchase', 'eddapi_send_zapier_webhook', 10, 1 );
function eddapi_send_zapier_webhook( $payment_id ) {
$payment = edd_get_payment( $payment_id );
$customer = new EDD_Customer( $payment->customer_id );
$payload = [
'payment_id' => $payment_id,
'order_number' => $payment->number,
'customer_name' => $customer->name,
'customer_email'=> $customer->email,
'total' => $payment->total,
'currency' => $payment->currency,
'products' => array_map( function( $item ) {
return [
'name' => $item['name'],
'price' => $item['price'],
'qty' => $item['quantity'],
];
}, $payment->cart_details ),
'purchase_date' => $payment->date,
'download_urls' => edd_get_purchase_download_links( $payment_id ),
];
wp_remote_post( 'https://hooks.zapier.com/hooks/catch/YOUR_ZAPIER_WEBHOOK_URL/', [
'method' => 'POST',
'headers' => [ 'Content-Type' => 'application/json' ],
'body' => wp_json_encode( $payload ),
'timeout' => 15,
'blocking' => false, // Fire-and-forget to avoid slowing checkout
] );
}
/**
* EDD REST API - React Native Mobile Storefront (Core Logic)
*
* A proxy backend (Node.js / WP REST handler) stores the API credentials.
* The mobile app calls YOUR proxy, not EDD directly.
* This file shows the client-side data layer.
*/
const PROXY_BASE = 'https://api.yourstore.com/edd'; // your backend proxy
class EddStoreApi {
constructor( authToken ) {
this.authToken = authToken; // user's app session token (NOT EDD key)
}
async get( path, params = {} ) {
const url = new URL( `${PROXY_BASE}${path}` );
Object.entries( params ).forEach( ( [ k, v ] ) => url.searchParams.set( k, v ) );
const res = await fetch( url.toString(), {
headers: {
'Authorization': `Bearer ${this.authToken}`,
'Content-Type': 'application/json',
},
} );
if ( res.status === 429 ) throw new Error( 'Too many requests. Please slow down.' );
if ( ! res.ok ) throw new Error( `Request failed: ${res.status}` );
return res.json();
}
// List all products
getProducts( page = 1 ) {
return this.get( '/products', { page, number: 20 } );
}
// Get a single product with pricing tiers
getProduct( id ) {
return this.get( `/products/${id}` );
}
// Get customer purchase history (requires authenticated user)
getPurchases( email ) {
return this.get( '/sales', { customer: email } );
}
// Get download links for a payment
getDownloads( paymentKey ) {
return this.get( `/payment-receipt`, { payment_key: paymentKey } );
}
}
// Example: Load product catalog
async function loadStorefront( api ) {
const data = await api.getProducts( 1 );
return data.products.map( p => ( {
id: p.info.id,
title: p.info.title,
price: p.pricing.amount,
image: p.info.thumbnail,
excerpt: p.info.excerpt,
} ) );
}
export default EddStoreApi;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment