@seom/sdk
v1.0.1
Published
Official Node.js SDK for the Seom SEO Content Generation API
Readme
@seom/sdk
Official Node.js / TypeScript SDK for the Seom SEO Content Generation API.
Requirements
- Node.js 18+ (uses native
fetch) - TypeScript 5.x (optional — works with plain JS too)
Installation
npm install @seom/sdk
# or
pnpm add @seom/sdk
# or
yarn add @seom/sdkQuick start
import { SeomClient } from '@seom/sdk'
// Get your API key from Settings → API Keys in the Seom dashboard
const client = new SeomClient({ apiKey: 'sk-seom-...' })
// List your last 10 completed articles
const { data, meta } = await client.articles.list({ status: 'DONE', limit: 10 })
console.log(`${meta.total} articles total`)
data.forEach(job => console.log(job.article?.title))Authentication
Create an API key in your workspace: Settings → API Keys → New API key.
Pass it when constructing the client:
const client = new SeomClient({ apiKey: 'sk-seom-your_key_here' })Or use an environment variable (recommended):
const client = new SeomClient({ apiKey: process.env.SEOM_API_KEY! })Usage
Articles
// List articles (paginated)
const { data, meta } = await client.articles.list({
status: 'DONE', // QUEUED | PROCESSING | DONE | FAILED
format: 'BLOG_ARTICLE', // BLOG_ARTICLE | LINKEDIN_POST | FACEBOOK_POST | TWITTER_THREAD | INSTAGRAM_CAPTION
page: 1,
limit: 20,
})
// Get one article with full HTML content
const { data: article } = await client.articles.get('job_abc123')
console.log(article.article?.htmlContent)
// Check generation status (for polling)
const { data: status } = await client.articles.status('job_abc123')
console.log(status.status, status.progress + '%', status.currentStep)
// Trigger generation (returns immediately with a jobId)
const { data: job } = await client.articles.generate({
keyword: 'best SEO tools 2025',
format: 'BLOG_ARTICLE', // optional, defaults to BLOG_ARTICLE
locale: 'EN_US', // VI | EN_US | EN_GB — defaults to workspace setting
})
console.log('Job queued:', job.jobId)
// Generate AND wait for it to finish (polls automatically)
const { data: result } = await client.articles.generateAndWait(
{ keyword: 'best SEO tools 2025', locale: 'EN_US' },
{
pollInterval: 5_000, // check every 5 seconds (default)
timeout: 600_000, // give up after 10 minutes (default)
},
)
console.log(result.article?.title)
console.log(result.article?.wordCount, 'words')
console.log(result.article?.htmlContent?.slice(0, 500))
// Wait for an already-queued job
const { data: finished } = await client.articles.waitFor('job_abc123')Keywords
// List keyword opportunities
const { data, meta } = await client.keywords.list({
priority: 'HIGH', // HIGH | MEDIUM | LOW
page: 1,
limit: 20,
})
data.forEach(kw => {
console.log(kw.keyword, `score: ${kw.opportunityScore}`)
})Workspace
const { data } = await client.workspace.get()
console.log(data.name)
console.log(data.plan.name) // "Basic"
console.log(data.usage.articlesThisMonth) // 8
console.log(data.usage.articlesLimit) // 30
console.log(data.usage.articlesRemaining) // 22Error handling
All API errors throw a SeomError:
import { SeomClient, SeomError } from '@seom/sdk'
try {
await client.articles.get('does-not-exist')
} catch (err) {
if (err instanceof SeomError) {
console.log(err.code) // 'NOT_FOUND'
console.log(err.message) // 'Article not found...'
console.log(err.statusCode) // 404
console.log(err.docs) // link to error docs
}
}Common error codes:
| Code | HTTP | Meaning |
|---|---|---|
| UNAUTHORIZED | 401 | Missing or invalid API key |
| FORBIDDEN | 403 | Key doesn't have the required scope |
| NOT_FOUND | 404 | Resource doesn't exist |
| VALIDATION_ERROR | 400 | Invalid request body |
| PAYMENT_REQUIRED | 402 | Monthly article limit reached — upgrade plan |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests |
| GENERATION_FAILED | 500 | AI generation failed (check server logs) |
| GENERATION_TIMEOUT | 408 | waitFor() timed out — job may still be running |
Pagination
All list methods return meta with pagination info:
const { data, meta } = await client.articles.list({ page: 1, limit: 20 })
console.log(meta.total) // 84 — total matching items
console.log(meta.page) // 1
console.log(meta.limit) // 20
console.log(meta.hasMore) // true — there are more pages
// Fetch all pages
let page = 1
const allArticles = []
while (true) {
const res = await client.articles.list({ page, limit: 50, status: 'DONE' })
allArticles.push(...res.data)
if (!res.meta.hasMore) break
page++
}Self-hosting / custom base URL
const client = new SeomClient({
apiKey: 'sk-seom-...',
baseUrl: 'http://localhost:4000/api', // your local dev server
})TypeScript
The SDK is written in TypeScript and ships with full type definitions. All response types are exported:
import type {
JobSummary,
ArticleFull,
KeywordOpportunity,
WorkspaceInfo,
SeomResponse,
SeomListResponse,
} from '@seom/sdk'Examples
See the examples/ directory:
list-articles.ts— paginate through all articlesgenerate-article.ts— generate and wait with error handlingkeyword-research.ts— trigger multiple jobs in parallel
API reference
Full API reference: seom.one/docs
License
MIT
