'Apply production-ready SalesLoft API patterns for TypeScript and Python.
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill salesloft-sdk-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Salesloft Sdk Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-salesloft-sdk-patterns-b42e1469)More formats (shields.io, HTML) on the badges page.
---
name: salesloft-sdk-patterns
description: 'Apply production-ready SalesLoft API patterns for TypeScript and Python.
Use when building SalesLoft integrations, implementing pagination,
or wrapping the REST API v2 with typed clients.
Trigger: "salesloft SDK patterns", "salesloft best practices", "salesloft client
wrapper".
'
allowed-tools: Read, Write, Edit
version: 1.6.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- sales
- outreach
- salesloft
compatibility: Designed for Claude Code
---
# SalesLoft SDK Patterns
## Overview
Production-ready patterns for the SalesLoft REST API v2. There is no official TypeScript/Python SDK -- build a typed wrapper around `https://api.salesloft.com/v2/` with automatic pagination, retry, and error normalization.
## Prerequisites
- Completed `salesloft-install-auth` setup
- `axios` or `node-fetch` installed
- Familiarity with async/await and generics
## Instructions
### Step 1: Typed API Client Singleton
```typescript
// src/salesloft/client.ts
import axios, { AxiosInstance, AxiosError } from 'axios';
interface SalesloftPaging {
per_page: number;
current_page: number;
total_pages: number;
total_count: number;
}
interface SalesloftListResponse<T> {
data: T[];
metadata: { paging: SalesloftPaging };
}
interface SalesloftSingleResponse<T> {
data: T;
}
let instance: AxiosInstance | null = null;
export function getSalesloftClient(): AxiosInstance {
if (!instance) {
instance = axios.create({
baseURL: process.env.SALESLOFT_BASE_URL || 'https://api.salesloft.com/v2',
headers: { Authorization: `Bearer ${process.env.SALESLOFT_API_KEY}` },
timeout: 30_000,
});
// Add response interceptor for rate-limit headers
instance.interceptors.response.use(undefined, handleRateLimitError);
}
return instance;
}
```
### Step 2: Automatic Pagination Iterator
```typescript
// SalesLoft paginates with page/per_page params, max 100 per page
async function* paginate<T>(
endpoint: string,
params: Record<string, any> = {},
): AsyncGenerator<T> {
const client = getSalesloftClient();
let page = 1;
let totalPages = 1;
do {
const { data: response } = await client.get<SalesloftListResponse<T>>(
endpoint, { params: { ...params, per_page: 100, page } }
);
for (const item of response.data) yield item;
totalPages = response.metadata.paging.total_pages;
page++;
} while (page <= totalPages);
}
// Usage: iterate all people
for await (const person of paginate<Person>('/people.json')) {
console.log(person.display_name);
}
```
### Step 3: Error Handling with Rate-Limit Awareness
```typescript
// SalesLoft uses cost-based rate limiting: 600 cost/min
// High-page requests (page > 100) cost 3-30 points instead of 1
async function handleRateLimitError(error: AxiosError) {
if (error.response?.status === 429) {
const retryAfter = parseInt(
error.response.headers['retry-after'] || '60', 10
);
console.warn(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
return getSalesloftClient().request(error.config!);
}
throw error;
}
class SalesloftApiError extends Error {
constructor(
message: string,
public status: number,
public retryable: boolean,
) {
super(message);
this.name = 'SalesloftApiError';
}
}
```
### Step 4: Python Equivalent
```python
import os, time, requests
from typing import Iterator, Any
class SalesloftClient:
BASE_URL = "https://api.salesloft.com/v2"
def __init__(self, api_key: str | None = None):
self.session = requests.Session()
self.session.headers["Authorization"] = f"Bearer {api_key or os.environ['SALESLOFT_API_KEY']}"
def get(self, endpoint: str, **params) -> dict:
resp = self.session.get(f"{self.BASE_URL}/{endpoint}", params=params)
if resp.status_code == 429:
time.sleep(int(resp.headers.get("Retry-After", 60)))
return self.get(endpoint, **params)
resp.raise_for_status()
return resp.json()
def paginate(self, endpoint: str, **params) -> Iterator[dict]:
page = 1
while True:
result = self.get(endpoint, page=page, per_page=100, **params)
yield from result["data"]
if page >= result["metadata"]["paging"]["total_pages"]:
break
page += 1
```
## Output
- Type-safe client singleton with rate-limit retry
- Automatic pagination that handles all pages
- Error normalization for consistent handling
- Python and TypeScript implementations
## Error Handling
| Status | Meaning | Retryable | Cost |
|--------|---------|-----------|------|
| `401` | Invalid/expired token | No (refresh token) | 0 |
| `404` | Resource not found | No | 1 |
| `422` | Validation error | No (fix payload) | 1 |
| `429` | Rate limited | Yes (wait `Retry-After`) | 0 |
| `5xx` | Server error | Yes (backoff) | 1 |
## Resources
- [SalesLoft API Basics](https://developers.salesloft.com/docs/platform/api-basics/)
- [Rate Limits](https://developers.salesloft.com/docs/platform/api-basics/rate-limits/)
- [People Endpoint](https://developers.salesloft.com/docs/api/people-index/)
## Next Steps
Apply patterns in `salesloft-core-workflow-a` for real-world usage.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!