Skakio Catalog API - LLM Quick Reference
For AI Assistants: This document provides everything needed to help developers integrate with the Skakio Catalog API. Copy code snippets directly - they are production-ready.
TL;DR - Minimal Working Example
// 1. Get token (exchange API key for JWT)
const tokenRes = await fetch('https://api.skakio.com/auth/token', {
method: 'POST',
headers: { 'X-API-Key': 'pk_live_YOUR_KEY' }
});
const { data: { token } } = await tokenRes.json();
// 2. Make API calls
const storeRes = await fetch('https://api.skakio.com/api/store/STORE_ID', {
headers: { 'Authorization': `Bearer ${token}` }
});
const { data: store } = await storeRes.json();
API Overview
| Item | Value |
|---|
| Base URL | https://api.skakio.com |
| Auth | API Key → JWT Token |
| Token TTL | 15 minutes (configurable 1-60 min) |
| Response Format | JSON with { code, status, data } wrapper |
API Key Types
| Prefix | Type | Use | Permissions |
|---|
pk_live_ | Public | Client-side (browsers, mobile) | Read-only |
sk_live_ | Secret | Server-side only | Full access |
pk_test_ | Test Public | Development | Read-only, test data |
sk_test_ | Test Secret | Development | Full access, test data |
Rate Limits
All /api/* endpoints require authentication. Rate limits vary by key type:
| Key Type | Per Minute | Per Hour |
|---|
| Public (pk_) | 100 | 1,000 |
| Secret (sk_) | 500 | 5,000 |
Authentication Flow
┌─────────────────────────────────────────────────────────────┐
│ POST /auth/token → Get JWT token (15 min TTL) │
│ POST /auth/refresh → Refresh existing token │
│ POST /auth/validate → Check token validity │
└─────────────────────────────────────────────────────────────┘
Get Token
curl -X POST https://api.skakio.com/auth/token \
-H "X-API-Key: pk_live_your_key"
Response:
{
"code": 200,
"status": "OK",
"data": {
"token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 900
}
}
Custom Token TTL
curl -X POST https://api.skakio.com/auth/token \
-H "X-API-Key: pk_live_your_key" \
-H "Content-Type: application/json" \
-d '{"ttl_minutes": 30}'
Refresh Token
curl -X POST https://api.skakio.com/auth/refresh \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
Core Endpoints
Catalog API (Read-Only, Requires Authentication)
| Endpoint | Description |
|---|
GET /api/store/:id | Get store details |
GET /api/store/:id/listings | Get store’s listings (categories) |
GET /api/store/:id/payment-methods | Get store’s payment methods |
GET /api/listing/:id | Get listing details |
GET /api/listing/:id?expand=publication | Get listing with products |
GET /api/publication/:id | Get product details |
GET /api/publication/:id/related | Get related products |
GET /api/publication/:id/reviews | Get product reviews |
GET /api/marketplace/search?q=term | Search products |
Query Parameters
| Param | Type | Description |
|---|
expand | string | Include related data: publication, listing, media |
page | number | Page number (default: 1) |
limit | number | Items per page (default: 20, max: 50) |
sort | string | Sort field: created_at, price, name |
order | string | Sort order: asc, desc |
Example: Get Store with Listings
curl "https://api.skakio.com/api/store/store_abc123?expand=listing" \
-H "Authorization: Bearer TOKEN"
Example: Get Listing with Products
curl "https://api.skakio.com/api/listing/list_xyz?expand=publication&limit=20" \
-H "Authorization: Bearer TOKEN"
Example: Search Products
curl "https://api.skakio.com/api/marketplace/search?q=sneakers&limit=10" \
-H "Authorization: Bearer TOKEN"
Response Structures
Store
interface Store {
id: string; // "store_abc123"
name: string;
description: string;
logo?: { url: string };
banner?: { url: string };
settings: {
currency: string; // "USD", "MXN", etc.
language: string;
};
listings?: Listing[]; // When expanded
}
Listing (Category)
interface Listing {
id: string; // "list_xyz789"
name: string;
description: string;
publicationCount: number;
media?: { url: string; alt?: string }[];
publications?: Publication[]; // When expanded
}
Publication (Product)
interface Publication {
id: string; // "pub_123abc"
title: string;
description: string;
price: {
amount: number; // 29.99
currency: string; // "USD"
};
quantity: {
available: number;
reserved: number;
};
media?: {
url: string;
alt?: string;
type: 'image' | 'video';
}[];
attributes?: {
key: string;
value: string;
}[];
}
interface PaginatedResponse<T> {
code: number;
status: string;
data: T[];
pagination: {
page: number;
limit: number;
totalItems: number;
totalPages: number;
};
}
Production-Ready Client Class
const API_BASE = 'https://api.skakio.com';
class SkakioClient {
private apiKey: string;
private token: string | null = null;
private tokenExpiresAt = 0;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
private async getToken(): Promise<string> {
const now = Date.now();
const buffer = 60_000; // 1 minute buffer
if (!this.token || now > this.tokenExpiresAt - buffer) {
const res = await fetch(`${API_BASE}/auth/token`, {
method: 'POST',
headers: { 'X-API-Key': this.apiKey },
});
if (!res.ok) {
const error = await res.json();
throw new Error(error.error?.message || 'Failed to get token');
}
const { data } = await res.json();
this.token = data.token;
this.tokenExpiresAt = now + data.expires_in * 1000;
}
return this.token!;
}
async fetch<T>(path: string): Promise<T> {
const token = await this.getToken();
const res = await fetch(`${API_BASE}${path}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (res.status === 401) {
// Token expired, force refresh
this.token = null;
return this.fetch(path);
}
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After') || '60';
throw new Error(`Rate limited. Retry after ${retryAfter}s`);
}
if (!res.ok) {
const error = await res.json();
throw new Error(error.error?.message || `API error: ${res.status}`);
}
const { data } = await res.json();
return data;
}
}
// Usage
const api = new SkakioClient('pk_live_your_key');
const store = await api.fetch<Store>('/api/store/store_abc123');
const listings = await api.fetch<Listing[]>('/api/store/store_abc123/listings');
const products = await api.fetch<Publication[]>('/api/listing/list_xyz?expand=publication');
Framework Integration Patterns
Next.js (App Router)
// lib/skakio.ts
const api = new SkakioClient(process.env.SKAKIO_API_KEY!);
export const STORE_ID = process.env.SKAKIO_STORE_ID!;
// app/page.tsx
export default async function Home() {
const store = await api.fetch<Store>(`/api/store/${STORE_ID}`);
return <h1>{store.name}</h1>;
}
Astro
---
// src/pages/index.astro
const api = new SkakioClient(import.meta.env.SKAKIO_API_KEY);
const store = await api.fetch(`/api/store/${import.meta.env.SKAKIO_STORE_ID}`);
---
<h1>{store.name}</h1>
React (Client-Side)
// Use public key only in client-side code
const api = new SkakioClient('pk_live_xxx');
function ProductList({ listingId }: { listingId: string }) {
const [products, setProducts] = useState<Publication[]>([]);
useEffect(() => {
api.fetch<Listing>(`/api/listing/${listingId}?expand=publication`)
.then(listing => setProducts(listing.publications || []));
}, [listingId]);
return products.map(p => <div key={p.id}>{p.title}</div>);
}
Error Handling
{
"code": 401,
"status": "error",
"error": {
"code": "TOKEN_EXPIRED",
"message": "Token has expired"
}
}
Common Error Codes
| HTTP | Code | Meaning | Action |
|---|
| 401 | TOKEN_EXPIRED | JWT expired | Refresh token |
| 401 | INVALID_API_KEY | Bad API key | Check key in Studio |
| 403 | STORE_ACCESS_DENIED | No store access | Check key scope |
| 404 | NOT_FOUND | Resource missing | Verify ID |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests | Wait and retry |
Error Handling Pattern
try {
const data = await api.fetch('/api/store/xxx');
} catch (err) {
if (err.message.includes('Rate limited')) {
// Wait and retry
} else if (err.message.includes('TOKEN_EXPIRED')) {
// Token auto-refreshed, retry
} else {
// Handle other errors
console.error(err);
}
}
Environment Variables
# .env.local (Next.js) or .env (other)
SKAKIO_API_KEY=pk_live_your_public_key_here
SKAKIO_STORE_ID=store_your_store_id_here
# For server-side only operations
SKAKIO_SECRET_KEY=sk_live_your_secret_key_here
Quick Checklist for Integration
- Get API Key - Go to
/studio/api-keys and create a key
- Store Key Securely - Use environment variables, never commit to git
- Exchange for Token - POST to
/auth/token with X-API-Key header
- Cache Token - Reuse until expiry, refresh proactively
- Handle 401 - Auto-refresh token on expiry
- Handle 429 - Implement backoff and respect
Retry-After
- Use Expand - Reduce requests by expanding related data
Common Use Cases
Build a Storefront
// 1. Get store info
const store = await api.fetch(`/api/store/${STORE_ID}`);
// 2. Get categories (listings)
const listings = await api.fetch(`/api/store/${STORE_ID}/listings`);
// 3. Get products in a category
const listing = await api.fetch(`/api/listing/${listingId}?expand=publication`);
// 4. Get single product
const product = await api.fetch(`/api/publication/${productId}`);
Search Products
const results = await api.fetch(
`/api/marketplace/search?q=${encodeURIComponent(query)}&limit=20`
);
Paginate Results
async function* getAllProducts(listingId: string) {
let page = 1;
let hasMore = true;
while (hasMore) {
const res = await api.fetch<PaginatedResponse<Publication>>(
`/api/listing/${listingId}/publications?page=${page}&limit=50`
);
yield* res.data;
hasMore = page < res.pagination.totalPages;
page++;
}
}
Links
- API Keys:
/studio/api-keys
- Interactive Reference:
/docs/api
- Rate Limits:
/docs/api/rate-limits
- Error Codes:
/docs/api/errors