SDKs
Official client libraries for the Agent Commerce Protocol. Install an SDK to start querying storefronts in minutes.
JavaScript / TypeScript
Full-featured SDK with TypeScript types, auto-retry, and streaming support.
Python
Pythonic SDK with type hints, async support, and automatic pagination.
SDK Comparison
Both SDKs provide full coverage of the Agent Commerce Protocol. Choose the one that matches your runtime.
| Feature | JS / TS | Python |
|---|---|---|
| Typed responses | TypeScript generics | Pydantic models |
| Async support | Native (Promise) | AsyncCresvaClient |
| Streaming | Yes | Yes |
| Auto-pagination | Yes | Yes |
| Auto-retry | Yes | Yes |
| Middleware / hooks | Yes | Yes |
| Batch operations | Yes | Yes |
| Negotiation | Yes | Yes |
| Trust scoring | Yes | Yes |
| Min runtime | Node 18+ | Python 3.9+ |
| Package size | ~42 KB gzipped | ~38 KB |
JavaScript / TypeScript SDK
Installation
The SDK requires Node.js 18 or later. Install it with your preferred package manager.
npm install @cresva/sdkTypeScript definitions are included in the package -- no separate @types install is needed.
Initialization
import { CresvaClient } from "@cresva/sdk";
const client = new CresvaClient({
apiKey: process.env.CRESVA_API_KEY, // pk_live_* or sk_live_*
brandId: "brand_abc123",
// Optional configuration:
timeout: 30000, // Request timeout in ms (default: 30s)
maxRetries: 3, // Auto-retry on transient errors (default: 3)
baseUrl: undefined, // Override base URL (for testing)
});Authentication
The SDK supports two key types. Use publishable keys (pk_live_*) for read-only operations in client-side code, and secret keys (sk_live_*) for write operations on the server side. Never expose secret keys to the browser.
// The SDK automatically reads CRESVA_API_KEY from process.env
// when no apiKey is passed explicitly.
const client = new CresvaClient({ brandId: "brand_abc123" });
// Explicit key (useful for multi-tenant setups)
const client2 = new CresvaClient({
apiKey: "sk_live_project_key_here",
brandId: "brand_abc123",
});
// Per-request key override
const results = await client.query(
{ intent: "search", query: "headphones" },
{ apiKey: "pk_live_different_key" }
);Configuration Options
Every option can be set at client initialization or overridden per request.
const client = new CresvaClient({
apiKey: process.env.CRESVA_API_KEY,
brandId: "brand_abc123",
// Timeout: max duration for a single HTTP request (ms)
timeout: 30000,
// Retries: the SDK retries on 429 and 5xx with exponential backoff
maxRetries: 3,
// Base URL: override for staging, proxies, or local development
baseUrl: "https://staging.cresva.ai",
// Custom headers: forwarded on every request
defaultHeaders: {
"X-Request-Source": "my-agent-v2",
"X-Correlation-Id": "corr_abc123",
},
// HTTP agent: supply your own for connection pooling or proxying
httpAgent: myCustomAgent,
});
// Override per request
const results = await client.query(
{ intent: "search", query: "headphones" },
{ timeout: 5000, maxRetries: 1 }
);Core Methods
// Search products
const results = await client.query({
intent: "search",
query: "wireless headphones under $200",
filters: { price: { max: 200, currency: "USD" } },
limit: 10,
});
// Get a product
const product = await client.products.get("prod_h7k2m");
// List products
const products = await client.products.list({ category: "electronics" });
// Get recommendations
const recs = await client.recommend("prod_h7k2m", { type: "similar" });
// Compare products
const comparison = await client.compare({
productIds: ["prod_h7k2m", "prod_x9y8z"],
});
// Negotiate
const negotiation = await client.negotiate({
action: "initiate",
productId: "prod_h7k2m",
offeredPrice: 149.99,
currency: "USD",
});
// Create transaction
const txn = await client.transactions.create({
items: [{ productId: "prod_h7k2m", quantity: 1, price: 164.99 }],
currency: "USD",
shippingAddress: { line1: "123 Main St", city: "SF", state: "CA", postalCode: "94102", country: "US" },
});
// Get trust score
const trust = await client.trust.get();TypeScript Types
Every request and response is fully typed. Import the types you need for strict type checking across your codebase.
import type {
AgentQueryRequest,
AgentQueryResponse,
AgentProductCard,
NegotiationRequest,
NegotiationResponse,
TransactionRequest,
TransactionResponse,
TrustScore,
} from "@cresva/sdk";interface AgentQueryRequest {
intent: "search" | "recommend" | "compare" | "detail";
query: string;
filters?: {
price?: { min?: number; max?: number; currency: string };
category?: string;
brand?: string;
attributes?: Record<string, string | string[]>;
};
limit?: number; // 1-100, default 20
offset?: number; // for pagination
sort?: "relevance" | "price_asc" | "price_desc" | "rating" | "newest";
}
interface AgentQueryResponse {
results: AgentProductCard[];
total: number;
hasMore: boolean;
cursor?: string;
meta: {
queryId: string;
latencyMs: number;
appliedFilters: Record<string, unknown>;
};
}
interface AgentProductCard {
id: string;
title: string;
description: string;
price: { amount: number; currency: string; formatted: string };
images: { url: string; alt: string; width: number; height: number }[];
rating?: { average: number; count: number };
availability: "in_stock" | "low_stock" | "out_of_stock" | "preorder";
attributes: Record<string, string>;
url: string;
}
interface NegotiationRequest {
action: "initiate" | "counter" | "accept" | "reject";
productId: string;
offeredPrice?: number;
currency: string;
negotiationId?: string; // required for counter/accept/reject
message?: string;
}
interface NegotiationResponse {
negotiationId: string;
status: "pending" | "countered" | "accepted" | "rejected" | "expired";
offeredPrice: number;
counterPrice?: number;
expiresAt: string; // ISO 8601
message?: string;
}
interface TransactionRequest {
items: { productId: string; quantity: number; price: number }[];
currency: string;
shippingAddress: {
line1: string;
line2?: string;
city: string;
state: string;
postalCode: string;
country: string;
};
negotiationId?: string; // attach a completed negotiation
metadata?: Record<string, string>;
}
interface TransactionResponse {
transactionId: string;
status: "created" | "processing" | "completed" | "failed" | "refunded";
total: { amount: number; currency: string; formatted: string };
items: { productId: string; quantity: number; unitPrice: number }[];
createdAt: string;
}
interface TrustScore {
brandId: string;
overall: number; // 0-100
tier: "unverified" | "bronze" | "silver" | "gold" | "platinum";
breakdown: {
returnPolicy: number;
fulfillmentSpeed: number;
customerSatisfaction: number;
dataTransparency: number;
negotiationFairness: number;
};
updatedAt: string;
}Error Handling
The SDK throws typed errors for every failure mode. All errors extend CresvaError, which includes the HTTP status code, a machine-readable error code, and a human-readable message.
import { CresvaError, RateLimitError, NotFoundError } from "@cresva/sdk";
try {
const results = await client.query({ intent: "search", query: "headphones" });
} catch (error) {
if (error instanceof RateLimitError) {
console.log("Rate limited, retry after:", error.retryAfter);
} else if (error instanceof NotFoundError) {
console.log("Product not found");
} else if (error instanceof CresvaError) {
console.log("API error:", error.code, error.message);
}
}import {
CresvaError, // Base class for all API errors
AuthenticationError, // 401 - invalid or missing API key
PermissionError, // 403 - key lacks required scope
NotFoundError, // 404 - resource does not exist
ValidationError, // 422 - request body failed validation
RateLimitError, // 429 - too many requests
InternalError, // 500 - server error (auto-retried)
ConnectionError, // Network-level failure (auto-retried)
TimeoutError, // Request exceeded timeout (auto-retried)
} from "@cresva/sdk";
// Every CresvaError exposes:
// error.status - HTTP status code (number)
// error.code - machine-readable code (string), e.g. "rate_limit_exceeded"
// error.message - human-readable message
// error.requestId - unique ID for support tickets
// error.headers - raw response headers
// Exhaustive error handling pattern
async function safeQuery(query: string) {
try {
return await client.query({ intent: "search", query });
} catch (error) {
if (error instanceof RateLimitError) {
// Back off and retry after the server-specified delay
await sleep(error.retryAfter * 1000);
return client.query({ intent: "search", query });
}
if (error instanceof ValidationError) {
// Log the field-level errors for debugging
console.error("Validation failed:", error.message);
for (const issue of error.issues ?? []) {
console.error(` ${issue.path}: ${issue.message}`);
}
throw error;
}
if (error instanceof AuthenticationError) {
console.error("Check your API key:", error.message);
throw error;
}
if (error instanceof CresvaError) {
console.error(`[Request ${error.requestId}] ${error.code}: ${error.message}`);
throw error;
}
// Non-API errors (network, etc.)
throw error;
}
}Middleware and Interceptors
Add middleware to observe, modify, or gate every request and response. Middleware runs in the order it is registered.
import { CresvaClient } from "@cresva/sdk";
const client = new CresvaClient({
apiKey: process.env.CRESVA_API_KEY,
brandId: "brand_abc123",
});
// Request middleware: runs before each HTTP call
client.use("request", async (req, next) => {
const start = Date.now();
console.log(`--> ${req.method} ${req.path}`);
// Modify the request (e.g. inject a tracing header)
req.headers["X-Trace-Id"] = crypto.randomUUID();
const res = await next(req);
console.log(`<-- ${res.status} (${Date.now() - start}ms)`);
return res;
});
// Response middleware: transform or log responses
client.use("response", async (res, next) => {
// Log rate limit headers for observability
const remaining = res.headers["x-ratelimit-remaining"];
if (remaining && Number(remaining) < 10) {
console.warn(`Rate limit warning: ${remaining} requests remaining`);
}
return next(res);
});
// Error middleware: centralized error handling
client.use("error", async (error, next) => {
// Report to your error tracking service
await reportToSentry(error);
return next(error);
});Pagination
The SDK supports both cursor-based and offset-based pagination. Use the async iterator for automatic page fetching.
// Automatic pagination with async iterator
for await (const product of client.products.list({ category: "electronics" })) {
console.log(product.title);
}
// Manual cursor-based pagination
let cursor: string | undefined;
do {
const page = await client.query({
intent: "search",
query: "headphones",
limit: 20,
...(cursor ? { cursor } : {}),
});
for (const result of page.results) {
process.stdout.write(result.title + "\n");
}
cursor = page.hasMore ? page.cursor : undefined;
} while (cursor);
// Collect all results into an array (caution: may be large)
const allProducts = await client.products.list({ category: "electronics" }).toArray();
console.log(`Fetched ${allProducts.length} products`);Streaming
For real-time use cases such as agent UIs, the SDK can stream query results as they arrive rather than waiting for the full response.
// Stream search results as they arrive
const stream = client.query.stream({
intent: "search",
query: "wireless headphones",
limit: 20,
});
stream.on("result", (product) => {
// Each product arrives individually as the server processes it
renderProductCard(product);
});
stream.on("meta", (meta) => {
console.log(`Query ${meta.queryId} completed in ${meta.latencyMs}ms`);
});
stream.on("error", (error) => {
console.error("Stream error:", error.message);
});
// Or use the async iterator
for await (const event of stream) {
if (event.type === "result") {
renderProductCard(event.data);
}
}Batch Operations
Fetch multiple resources in a single round-trip. The batch API reduces latency and counts as a single rate-limit hit.
// Batch product lookups
const products = await client.products.getBatch([
"prod_h7k2m",
"prod_x9y8z",
"prod_a1b2c",
]);
// Returns: AgentProductCard[] in the same order as the input IDs.
// Missing products are returned as null.
// Batch queries: run multiple intents in parallel server-side
const [searchResults, recommendations] = await client.batch([
{ method: "query", params: { intent: "search", query: "headphones" } },
{ method: "recommend", params: { productId: "prod_h7k2m", type: "similar" } },
]);
// Batch with error isolation: one failure does not abort others
const results = await client.batch(
[
{ method: "products.get", params: { id: "prod_h7k2m" } },
{ method: "products.get", params: { id: "prod_invalid" } },
{ method: "trust.get", params: {} },
],
{ failureMode: "isolate" } // default is "abort"
);
// results[0] -> { status: "success", data: AgentProductCard }
// results[1] -> { status: "error", error: NotFoundError }
// results[2] -> { status: "success", data: TrustScore }Testing
The SDK ships with testing utilities so you can mock API responses without hitting the network.
import { CresvaClient, createMockClient } from "@cresva/sdk/testing";
// createMockClient returns a fully-typed client where every method
// is a jest.fn() / vi.fn() that you can stub.
const mockClient = createMockClient();
mockClient.query.mockResolvedValue({
results: [
{
id: "prod_test1",
title: "Mock Headphones",
price: { amount: 99.99, currency: "USD", formatted: "$99.99" },
availability: "in_stock",
description: "Great headphones for testing",
images: [],
attributes: {},
url: "https://example.com/mock",
},
],
total: 1,
hasMore: false,
meta: { queryId: "q_test", latencyMs: 12, appliedFilters: {} },
});
// Inject the mock into your application code
const results = await mockClient.query({
intent: "search",
query: "headphones",
});
expect(results.results).toHaveLength(1);
expect(mockClient.query).toHaveBeenCalledWith(
expect.objectContaining({ intent: "search" })
);import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";
import { CresvaClient } from "@cresva/sdk";
const server = setupServer(
http.post("https://api.cresva.ai/api/storefront/*/query", () => {
return HttpResponse.json({
results: [{ id: "prod_mock", title: "MSW Mock Product" }],
total: 1,
hasMore: false,
meta: { queryId: "q_mock", latencyMs: 5, appliedFilters: {} },
});
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test("searches products", async () => {
const client = new CresvaClient({
apiKey: "pk_test_fake",
brandId: "brand_test",
});
const results = await client.query({
intent: "search",
query: "headphones",
});
expect(results.total).toBe(1);
});Python SDK
Installation
The SDK requires Python 3.9 or later. Install it with your preferred package manager.
pip install cresvaThe package includes inline type stubs (PEP 561), so type checkers like mypy and pyright work out of the box.
Initialization
from cresva import CresvaClient
client = CresvaClient(
api_key="pk_live_your_key_here", # or use CRESVA_API_KEY env var
brand_id="brand_abc123",
# Optional:
timeout=30, # Request timeout in seconds
max_retries=3, # Auto-retry on transient errors
)
# Async client
from cresva import AsyncCresvaClient
async_client = AsyncCresvaClient(
api_key="pk_live_your_key_here",
brand_id="brand_abc123",
)Authentication
Like the JavaScript SDK, the Python SDK supports publishable keys for read-only operations and secret keys for write operations. The client reads CRESVA_API_KEY from the environment automatically.
import os
# Automatic: reads CRESVA_API_KEY from environment
client = CresvaClient(brand_id="brand_abc123")
# Explicit key
client = CresvaClient(
api_key="sk_live_project_key_here",
brand_id="brand_abc123",
)
# Per-request key override
results = client.query(
intent="search",
query="headphones",
request_options={"api_key": "pk_live_different_key"},
)Configuration Options
import httpx
from cresva import CresvaClient
client = CresvaClient(
api_key="sk_live_your_key_here",
brand_id="brand_abc123",
# Timeout: max duration for a single HTTP request (seconds)
timeout=30,
# Retries: the SDK retries on 429 and 5xx with exponential backoff
max_retries=3,
# Base URL: override for staging, proxies, or local development
base_url="https://staging.cresva.ai",
# Custom headers: forwarded on every request
default_headers={
"X-Request-Source": "my-agent-v2",
"X-Correlation-Id": "corr_abc123",
},
# HTTP client: supply your own httpx.Client for proxying or custom TLS
http_client=httpx.Client(proxy="http://proxy.internal:8080"),
)
# Override per request
results = client.query(
intent="search",
query="headphones",
request_options={"timeout": 5, "max_retries": 1},
)Core Methods
# Search products
results = client.query(
intent="search",
query="wireless headphones under $200",
filters={"price": {"max": 200, "currency": "USD"}},
limit=10,
)
# Get a product
product = client.products.get("prod_h7k2m")
# List products with automatic pagination
for product in client.products.list(category="electronics"):
print(product.title, product.price)
# Negotiate
negotiation = client.negotiate(
action="initiate",
product_id="prod_h7k2m",
offered_price=149.99,
currency="USD",
)
# Create transaction
txn = client.transactions.create(
items=[{"product_id": "prod_h7k2m", "quantity": 1, "price": 164.99}],
currency="USD",
shipping_address={
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US",
},
)
# Get trust score
trust = client.trust.get()
print(f"Trust: {trust.overall}/100 ({trust.tier})")Type Hints and Models
All response objects are Pydantic models with full type annotations. This gives you autocompletion, runtime validation, and serialization for free.
from cresva.types import (
AgentQueryRequest,
AgentQueryResponse,
AgentProductCard,
NegotiationRequest,
NegotiationResponse,
TransactionRequest,
TransactionResponse,
TrustScore,
Price,
ShippingAddress,
)
# Response objects are typed Pydantic models
results: AgentQueryResponse = client.query(
intent="search",
query="headphones",
)
# Access fields with type safety
for product in results.results:
# product is AgentProductCard
title: str = product.title
price: Price = product.price
print(f"{title}: {price.formatted}")
# Serialize to dict or JSON
product_dict = product.model_dump()
product_json = product.model_dump_json()
# Construct request objects explicitly (useful for validation)
request = AgentQueryRequest(
intent="search",
query="headphones",
filters={"price": {"max": 200, "currency": "USD"}},
limit=10,
)
results = client.query(**request.model_dump())Error Handling
from cresva.errors import CresvaError, RateLimitError, NotFoundError
try:
results = client.query(intent="search", query="headphones")
except RateLimitError as e:
print(f"Rate limited, retry after: {e.retry_after}s")
except NotFoundError:
print("Product not found")
except CresvaError as e:
print(f"API error: {e.code} - {e.message}")from cresva.errors import (
CresvaError, # Base class for all API errors
AuthenticationError, # 401 - invalid or missing API key
PermissionError, # 403 - key lacks required scope
NotFoundError, # 404 - resource does not exist
ValidationError, # 422 - request body failed validation
RateLimitError, # 429 - too many requests
InternalError, # 500 - server error (auto-retried)
ConnectionError, # Network-level failure (auto-retried)
TimeoutError, # Request exceeded timeout (auto-retried)
)
# Every CresvaError exposes:
# error.status - HTTP status code (int)
# error.code - machine-readable code (str), e.g. "rate_limit_exceeded"
# error.message - human-readable message
# error.request_id - unique ID for support tickets
# error.headers - raw response headers (dict)
import time
def safe_query(query: str) -> AgentQueryResponse:
try:
return client.query(intent="search", query=query)
except RateLimitError as e:
time.sleep(e.retry_after)
return client.query(intent="search", query=query)
except ValidationError as e:
print(f"Validation failed: {e.message}")
for issue in e.issues or []:
print(f" {issue['path']}: {issue['message']}")
raise
except AuthenticationError as e:
print(f"Check your API key: {e.message}")
raise
except CresvaError as e:
print(f"[Request {e.request_id}] {e.code}: {e.message}")
raiseMiddleware and Hooks
The Python SDK uses a hook system for request/response interception. Hooks receive and return the request or response object.
import time
import uuid
from cresva import CresvaClient
client = CresvaClient(
api_key="sk_live_your_key_here",
brand_id="brand_abc123",
)
# Request hook: runs before each HTTP call
@client.on("request")
def log_request(req):
req.headers["X-Trace-Id"] = str(uuid.uuid4())
print(f"--> {req.method} {req.path}")
req._start_time = time.time()
return req
# Response hook: runs after each HTTP response
@client.on("response")
def log_response(res):
elapsed = time.time() - res.request._start_time
print(f"<-- {res.status_code} ({elapsed:.2f}s)")
remaining = res.headers.get("x-ratelimit-remaining")
if remaining and int(remaining) < 10:
print(f"Rate limit warning: {remaining} requests remaining")
return res
# Error hook: centralized error handling
@client.on("error")
def report_error(error):
sentry_sdk.capture_exception(error)
return errorPagination
# Automatic pagination with iterator (sync)
for product in client.products.list(category="electronics"):
print(product.title)
# Automatic pagination with async iterator
async for product in async_client.products.list(category="electronics"):
print(product.title)
# Manual cursor-based pagination
cursor = None
while True:
page = client.query(
intent="search",
query="headphones",
limit=20,
**({"cursor": cursor} if cursor else {}),
)
for result in page.results:
print(result.title)
if not page.has_more:
break
cursor = page.cursor
# Collect all results into a list
all_products = list(client.products.list(category="electronics"))
print(f"Fetched {len(all_products)} products")Streaming
# Stream search results as they arrive (sync)
with client.query.stream(intent="search", query="wireless headphones", limit=20) as stream:
for event in stream:
if event.type == "result":
print(f"Got product: {event.data.title}")
elif event.type == "meta":
print(f"Query {event.data.query_id} completed in {event.data.latency_ms}ms")
# Async streaming
async with async_client.query.stream(intent="search", query="headphones") as stream:
async for event in stream:
if event.type == "result":
await render_product_card(event.data)Batch Operations
# Batch product lookups
products = client.products.get_batch([
"prod_h7k2m",
"prod_x9y8z",
"prod_a1b2c",
])
# Returns: list[AgentProductCard | None] in input order
# Batch queries: run multiple intents in parallel server-side
search_results, recommendations = client.batch([
{"method": "query", "params": {"intent": "search", "query": "headphones"}},
{"method": "recommend", "params": {"product_id": "prod_h7k2m", "type": "similar"}},
])
# Batch with error isolation
results = client.batch(
[
{"method": "products.get", "params": {"id": "prod_h7k2m"}},
{"method": "products.get", "params": {"id": "prod_invalid"}},
{"method": "trust.get", "params": {}},
],
failure_mode="isolate", # default is "abort"
)
# results[0] -> BatchSuccess(data=AgentProductCard(...))
# results[1] -> BatchError(error=NotFoundError(...))
# results[2] -> BatchSuccess(data=TrustScore(...))Testing
The SDK includes test utilities for mocking API responses without network calls.
from cresva.testing import create_mock_client, mock_response
from cresva.types import AgentQueryResponse, AgentProductCard, Price
# create_mock_client returns a client where every method is a MagicMock
mock_client = create_mock_client()
mock_client.query.return_value = AgentQueryResponse(
results=[
AgentProductCard(
id="prod_test1",
title="Mock Headphones",
description="Great headphones for testing",
price=Price(amount=99.99, currency="USD", formatted="$99.99"),
images=[],
availability="in_stock",
attributes={},
url="https://example.com/mock",
),
],
total=1,
has_more=False,
meta={"query_id": "q_test", "latency_ms": 12, "applied_filters": {}},
)
# Use the mock in your tests
results = mock_client.query(intent="search", query="headphones")
assert len(results.results) == 1
mock_client.query.assert_called_once()import respx
import httpx
import pytest
from cresva import CresvaClient
@respx.mock
def test_search_products():
respx.post("https://api.cresva.ai/api/storefront/brand_test/query").mock(
return_value=httpx.Response(
200,
json={
"results": [{"id": "prod_mock", "title": "Mock Product"}],
"total": 1,
"has_more": False,
"meta": {"query_id": "q_mock", "latency_ms": 5, "applied_filters": {}},
},
)
)
client = CresvaClient(
api_key="pk_test_fake",
brand_id="brand_test",
)
results = client.query(intent="search", query="headphones")
assert results.total == 1cURL Examples
Every endpoint can be accessed directly with cURL. Replace pk_live_your_key_here with your API key and brand_abc123 with your brand ID.
Search Products
curl "https://api.cresva.ai/api/storefront/brand_abc123/search?q=wireless+headphones&max_price=200" \
-H "Authorization: Bearer pk_live_your_key_here"Query Products
curl -X POST "https://api.cresva.ai/api/storefront/brand_abc123/query" \
-H "Authorization: Bearer pk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"intent": "search",
"query": "noise-cancelling headphones",
"filters": {"price": {"max": 200, "currency": "USD"}},
"limit": 5
}'Initiate Negotiation
curl -X POST "https://api.cresva.ai/api/storefront/brand_abc123/negotiate" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"action": "initiate",
"product_id": "prod_h7k2m",
"offered_price": 149.99,
"currency": "USD"
}'Check Health
curl "https://api.cresva.ai/api/storefront/brand_abc123/health"Migration Guide: v1 to v2
SDK v2 introduces a cleaner API surface, improved type safety, and new features like streaming and batch operations. This guide covers every breaking change.
Client Initialization
The constructor options were renamed for consistency across SDKs.
// v1 - DEPRECATED
import Cresva from "@cresva/sdk";
const client = new Cresva({
key: "pk_live_...",
brand: "brand_abc123",
retries: 3,
timeoutMs: 30000,
});Query Method
The search() method was replaced by the unified query() method that supports multiple intents.
// v1 - DEPRECATED
const results = await client.search("headphones", {
maxPrice: 200,
currency: "USD",
});Error Classes
Error class names were made more specific. The generic APIError was replaced with CresvaError and its subclasses.
// v1 - DEPRECATED
import { APIError } from "@cresva/sdk";
try {
await client.search("headphones");
} catch (e) {
if (e instanceof APIError && e.status === 429) {
// handle rate limit
}
}Response Shape
Response objects are now typed classes instead of plain objects. Property access remains the same, but serialization methods have changed.
// v1 - responses were plain objects
const results = await client.search("headphones");
const data = JSON.parse(JSON.stringify(results));Python-Specific Changes
# v1 - DEPRECATED
from cresva import Cresva
client = Cresva(key="pk_live_...", brand="brand_abc123")
results = client.search("headphones", max_price=200)
# v2
from cresva import CresvaClient
client = CresvaClient(api_key="pk_live_...", brand_id="brand_abc123")
results = client.query(
intent="search",
query="headphones",
filters={"price": {"max": 200, "currency": "USD"}},
)
# v1 - manual pagination
page = client.products.list(page=1, per_page=20)
next_page = client.products.list(page=2, per_page=20)
# v2 - automatic pagination via iterator
for product in client.products.list(category="electronics"):
print(product.title)
# v1 - dict responses
results["products"][0]["title"]
# v2 - typed model responses
results.results[0].titleFull Changelog
- BreakingRenamed constructor:
CresvatoCresvaClient - BreakingRenamed option:
keytoapiKey(JS) /api_key(Python) - BreakingRenamed option:
brandtobrandId(JS) /brand_id(Python) - BreakingReplaced
search()withquery() - BreakingReplaced
APIErrorwithCresvaErrorhierarchy - BreakingResponse objects are now typed classes, not plain dicts/objects
- NewStreaming support via
client.query.stream() - NewBatch operations via
client.batch() - NewAutomatic pagination with async iterators
- NewMiddleware / hooks system
- NewBuilt-in test utilities (
@cresva/sdk/testing/cresva.testing) - NewPer-request option overrides