---
title: "Query and export contacts with rich filtering. Filter by connection/response status, campaign, custom attrs, or tags. Re"
method: GET
path: "/api/v1/contacts"
tags: ["contacts"]
---

# Query and export contacts with rich filtering. Filter by connection/response status, campaign, custom attrs, or tags. Re

`GET /api/v1/contacts`

## Request

- Base URL: `https://crispy.sh`
- URL: `https://crispy.sh/api/v1/contacts`
- Auth: HTTP bearer

## Query parameters

- `list_id` string: Scope to a specific list ID (omit for all contacts)
- `connection_status` 'connected' | 'not_connected' | 'invitation_pending' | 'invitation_declined' | 'invitation_withdrawn' | 'unknown': Filter by connection status
- `response_status` 'responded' | 'awaiting_response' | 'never_messaged': Filter by response status (computed from message timestamps)
- `in_campaign` boolean: true = contacts currently in a campaign; false = not in any campaign
- `campaign_id` string: Filter to contacts in a specific campaign
- `last_touched_before` string: ISO timestamp — contacts last touched before this date
- `last_touched_after` string: ISO timestamp — contacts last touched after this date
- `custom_attrs` object: Exact-match filter on custom attrs (e.g. {industry: 'SaaS'})
- `tags` string[]: Any-match filter on tags (OR)
- `tag_ids` string[]: Filter contacts by tag IDs (from list_tags). OR-match against contact_tags join table.
- `format` 'json' | 'csv': Response format (default json)
- `limit` number: Results per page (default 100, max 1000)
- `cursor` string: Pagination cursor from previous response

## Response `200`

Success

## Other responses

- `401`: Missing or expired API key
- `403`: Insufficient key scope or permission denied
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/crispy/apis/crispy-rest-api.md) · [All operations](https://skmtc.dev/crispy/apis/crispy-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/crispy/apis/crispy-rest-api/revisions/6bed0974d9ce?raw)
