TypeScript SDK
The official Orsa TypeScript SDK for Node.js and edge runtimes.
Installation
npm install @orsa.dev/sdk
# or
bun add @orsa.dev/sdk
# or
pnpm add @orsa.dev/sdk
# or
yarn add @orsa.dev/sdkRequirements: Node.js 18+ or any runtime with native fetch. Ships ESM + CJS + TypeScript types.
Playground
The repo ships a local Next.js sandbox at apps/sdk-playground that exercises every SDK method against a running API — useful both as a working reference and a place to feel out the developer experience. From the monorepo root:
cp apps/sdk-playground/.env.example apps/sdk-playground/.env.local
# add ORSA_API_KEY=or_live_…
bun --filter='@orsa/sdk-playground' run dev
# → http://localhost:3300The /brand, /web, and /ai pages give you a form-driven UI for each method; /errors demonstrates the four Orsa*Error classes live; /dx-notes is a copy-paste-ready DX checklist.
Quick Start
import Orsa from '@orsa.dev/sdk';
const client = new Orsa({ apiKey: process.env.ORSA_API_KEY! });
const brand = await client.brand.retrieve({ domain: 'stripe.com' });
console.log(brand.title); // "Stripe"
console.log(brand.colors[0]); // { hex: "#635BFF", name: "Iris" }Every method returns the unwrapped response payload — the { data, _meta } envelope is unpacked for you.
Configuration
const client = new Orsa({
apiKey: 'or_live_...', // Required
baseUrl: 'https://api.orsa.dev', // Optional (default)
timeout: 30_000, // Per-request timeout in ms
maxRetries: 3, // Retry on 429/5xx and connection errors
userAgent: 'my-app/1.0', // Appended after the SDK identifier
});| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | Your Orsa API key (required) |
baseUrl | string | https://api.orsa.dev | API base URL |
timeout | number | 30_000 | Request timeout in ms |
maxRetries | number | 3 | Max retries on 429/5xx + connection errors |
userAgent | string | — | Optional suffix appended to the SDK User-Agent |
Per-call overrides via RequestOptions:
const ac = new AbortController();
await client.brand.retrieve(
{ domain: 'stripe.com' },
{ signal: ac.signal, timeout: 5_000, headers: { 'x-correlation-id': 'abc' } },
);Namespaces
client.brand — Brand Intelligence
// Full brand record (Context.dev-shaped: array colors/logos/socials + *_legacy mirrors)
const brand = await client.brand.retrieve({ domain: 'stripe.com' });
// Aliases — legacy dict-shaped Brand
const byDomain = await client.brand.retrieveByDomain({ domain: 'stripe.com' });
const byEmail = await client.brand.retrieveByEmail({ email: 'support@stripe.com' });
const byTicker = await client.brand.retrieveByTicker({ ticker: 'AAPL' });
const byISIN = await client.brand.retrieveByISIN({ isin: 'US0378331005' });
// Fuzzy name match — returns { brand, matches: [{ id, domain, title, similarity }] }
const byName = await client.brand.retrieveByName({ name: 'Stripe' });
// Lightweight profile — just domain/title/logo/primaryColor/industries
const simple = await client.brand.retrieveSimplified({ domain: 'linear.app' });
// Inline base64 PNG screenshot
const shot = await client.brand.screenshot({ domain: 'stripe.com' });
// Design system: W3C-DTCG tokens + components + paste-ready DESIGN.md
const guide = await client.brand.styleguide({ domain: 'stripe.com' });
// Fonts only — same browser pool, half the cost
const fonts = await client.brand.fonts({ domain: 'stripe.com' });
// Industry classification (industries array, primary_industry)
const naics = await client.brand.naics({ domain: 'stripe.com' });
// Identify the merchant behind a bank transaction descriptor
const txn = await client.brand.transactionIdentifier({
transactionInfo: 'SQ *VERCEL INC 555-1234 CA',
});client.web — Web Scraping
// Single-page scrape. mode defaults to 'markdown' — pass 'html' or 'text' for those.
const page = await client.web.scrape({ url: 'https://stripe.com/docs' });
console.log(page.markdown);
const raw = await client.web.scrape({ url: 'https://stripe.com', mode: 'html' });
const text = await client.web.scrape({ url: 'https://stripe.com', mode: 'text' });
// Image extraction with role classification (logo / hero / product / icon / decorative)
const images = await client.web.scrapeImages({ url: 'https://stripe.com' });
const logos = images.images.filter((i) => i.role === 'logo');
// Sitemap discovery — reads robots.txt, walks sitemap-index files,
// returns up to 1,000 URLs plus path-grouped buckets
const sitemap = await client.web.scrapeSitemap({ domain: 'stripe.com' });
console.log(sitemap.groups.docs?.count);client.ai — AI Extraction
// Natural-language prompt against a domain. `result` is a string; ask for
// JSON explicitly if you want structured output.
const ans = await client.ai.query({
domain: 'stripe.com',
dataToExtract: 'Return the pricing plans as a JSON array.',
});
console.log(ans.result);
console.log(ans.usage.input_tokens);
// Tool-use-enforced product extraction from the homepage
const products = await client.ai.products({ domain: 'linear.app' });
for (const p of products.products) {
console.log(p.name, p.pricing?.amount);
}Error Handling
All errors extend OrsaError:
import {
OrsaError, // base class
OrsaAPIError, // 4xx/5xx response — has .status, .errorCode, .requestId
OrsaTimeoutError, // request exceeded the timeout / was aborted
OrsaConnectionError, // network failure after retries
} from '@orsa.dev/sdk';
try {
await client.brand.retrieve({ domain: 'stripe.com' });
} catch (err) {
if (err instanceof OrsaAPIError && err.status === 404) {
console.log('No brand cached yet — request /retrieve first.');
} else if (err instanceof OrsaTimeoutError) {
console.log('Took too long.');
} else if (err instanceof OrsaConnectionError) {
console.log('Network failure.');
} else {
throw err;
}
}Retries are automatic for 429, 500, 502, 503 and connection errors (exponential backoff, honors Retry-After). 4xx errors throw immediately.
TypeScript Types
All response and parameter types are exported:
import type {
// Config
OrsaConfig,
RequestOptions,
ResponseMeta,
// Brand
Brand,
BrandContextDev,
SimplifiedBrand,
BrandSearchResult,
// Visual
Styleguide,
FontsResult,
Screenshot,
// Classification
NaicsResult,
TransactionIdentifierResult,
// Scraping
ScrapeResult,
ScrapeMode,
ScrapeImagesResult,
ScrapeSitemapResult,
ScrapedImage,
ImageRole,
// AI
AIQueryResult,
AIProductsResult,
ExtractedProduct,
} from '@orsa.dev/sdk';Framework Examples
Next.js Server Component
import Orsa from '@orsa.dev/sdk';
const client = new Orsa({ apiKey: process.env.ORSA_API_KEY! });
export default async function BrandPage({ params }: { params: { domain: string } }) {
const brand = await client.brand.retrieve({ domain: params.domain });
return (
<div>
<h1>{brand.title}</h1>
<p>{brand.description}</p>
<div style={{ background: brand.colors[0]?.hex }} />
</div>
);
}Hono / Express
import Orsa from '@orsa.dev/sdk';
import { Hono } from 'hono';
const app = new Hono();
const orsa = new Orsa({ apiKey: process.env.ORSA_API_KEY! });
app.get('/brand/:domain', async (c) => {
const brand = await orsa.brand.retrieve({ domain: c.req.param('domain') });
return c.json(brand);
});