Introduction
# Keap
Access the Keap API with managed OAuth authentication. Manage contacts, companies, tags, tasks, orders, opportunities, campaigns, and more for CRM and marketing automation.
## Quick Start
```bash # List contacts python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/keap/crm/rest/v2/contacts?page_size=10') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
## Base URL
``` https://gateway.maton.ai/keap/crm/rest/{api-path} ```
The gateway proxies requests to `api.infusionsoft.com/crm/rest` and automatically injects your OAuth token.
## Authentication
All requests require the Maton API key in the Authorization header:
``` Authorization: Bearer $MATON_API_KEY ```
**Environment Variable:** Set your API key as `MATON_API_KEY`:
```bash export MATON_API_KEY="YOUR_API_KEY" ```
### Getting Your API Key
1. Sign in or create an account at [maton.ai](https://maton.ai) 2. Go to [maton.ai/settings](https://maton.ai/settings) 3. Copy your API key
## Connection Management
Manage your Keap OAuth connections at `https://ctrl.maton.ai`.
### List Connections
```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections?app=keap&status=ACTIVE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
### Create Connection
```bash python <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'keap'}).encode() req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
### Get Connection
```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
**Response:** ```json { "connection": { "connection_id": "d5242090-02ae-4195-83e3-8deca823eb9a", "status": "ACTIVE", "creation_time": "2026-02-08T01:34:44.738374Z", "last_updated_time": "2026-02-08T01:35:20.106942Z", "url": "https://connect.maton.ai/?session_token=...", "app": "keap", "metadata": {} } } ```
Open the returned `url` in a browser to complete OAuth authorization.
### Delete Connection
```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
### Specifying Connection
If you have multiple Keap connections, specify which one to use with the `Maton-Connection` header:
```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/keap/crm/rest/v2/contacts') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Maton-Connection', 'd5242090-02ae-4195-83e3-8deca823eb9a') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
If omitted, the gateway uses the default (oldest) active connection.
## API Reference
### User Info
#### Get Current User
```bash GET /keap/crm/rest/v2/oauth/connect/userinfo ```
**Response:** ```json { "email": "[email protected]", "sub": "1", "id": "4236128", "keap_id": "[email protected]", "family_name": "Doe", "given_name": "John", "is_admin": true } ```
### Contact Operations
#### List Contacts
```bash GET /keap/crm/rest/v2/contacts ```
Query parameters: - `page_size` - Number of results per page (default 50, max 1000) - `page_token` - Token for next page - `filter` - Filter expression - `order_by` - Sort order - `fields` - Fields to include in response
**Response:** ```json { "contacts": [ { "id": "9", "family_name": "Park", "given_name": "John" } ], "next_page_token": "" } ```
#### Get Contact
```bash GET /keap/crm/rest/v2/contacts/{contact_id} ```
#### Create Contact
```bash POST /keap/crm/rest/v2/contacts Content-Type: application/json
{ "given_name": "John", "family_name": "Doe", "email_addresses": [ {"email": "[email protected]", "field": "EMAIL1"} ], "phone_numbers": [ {"number": "555-1234", "field": "PHONE1"} ] } ```
**Response:** ```json { "id": "13", "family_name": "Doe", "given_name": "John" } ```
#### Update Contact
```bash PATCH /keap/crm/rest/v2/contacts/{contact_id} Content-Type: application/json
{ "given_name": "Jane" } ```
#### Delete Contact
```bash DELETE /keap/crm/rest/v2/contacts/{contact_id} ```
Returns 204 on success.
#### Get Contact Notes
```bash GET /keap/crm/rest/v2/contacts/{contact_id}/notes ```
#### Create Contact Note
```bash POST /keap/crm/rest/v2/contacts/{contact_id}/notes Content-Type: application/json
{ "body": "Note content here", "title": "Note Title" } ```
### Company Operations
#### List Companies
```bash GET /keap/crm/rest/v2/companies ```
#### Get Company
```bash GET /keap/crm/rest/v2/companies/{company_id} ```
#### Create Company
```bash POST /keap/crm/rest/v2/companies Content-Type: application/json
{ "company_name": "Acme Corp", "phone_number": {"number": "555-1234", "type": "MAIN"}, "website": "https://acme.com" } ```
#### Update Company
```bash PATCH /keap/crm/rest/v2/companies/{company_id} Content-Type: application/json
{ "company_name": "Acme Corporation" } ```
#### Delete Company
```bash DELETE /keap/crm/rest/v2/companies/{company_id} ```
### Tag Operations
#### List Tags
```bash GET /keap/crm/rest/v2/tags ```
**Response:** ```json { "tags": [ { "id": "91", "name": "Nurture Subscriber", "description": "", "category": {"id": "10"}, "create_time": "2017-04-24T17:26:26Z", "update_time": "2017-04-24T17:26:26Z" } ], "next_page_token": "" } ```
#### Get Tag
```bash GET /keap/crm/rest/v2/tags/{tag_id} ```
#### Create Tag
```bash POST /keap/crm/rest/v2/tags Content-Type: application/json
{ "name": "VIP Customer", "description": "High value customers" } ```
#### Update Tag
```bash PATCH /keap/crm/rest/v2/tags/{tag_id} Content-Type: application/json
{ "name": "Premium Customer" } ```
#### Delete Tag
```bash DELETE /keap/crm/rest/v2/tags/{tag_id} ```
#### List Contacts with Tag
```bash GET /keap/crm/rest/v2/tags/{tag_id}/contacts ```
#### Apply Tags to Contacts
```bash POST /keap/crm/rest/v2/tags/{tag_id}/contacts:applyTags Content-Type: application/json
{ "contact_ids": ["1", "2", "3"] } ```
#### Remove Tags from Contacts
```bash POST /keap/crm/rest/v2/tags/{tag_id}/contacts:removeTags Content-Type: application/json
{ "contact_ids": ["1", "2", "3"] } ```
### Tag Category Operations
#### List Tag Categories
```bash GET /keap/crm/rest/v2/tags/categories ```
#### Create Tag Category
```bash POST /keap/crm/rest/v2/tags/categories Content-Type: application/json
{ "name": "Customer Segments" } ```
### Task Operations
#### List Tasks
```bash GET /keap/crm/rest/v2/tasks ```
#### Get Task
```bash GET /keap/crm/rest/v2/tasks/{task_id} ```
#### Create Task
```bash POST /keap/crm/rest/v2/tasks Content-Type: application/json
{ "title": "Follow up call", "description": "Call to discuss proposal", "due_date": "2026-02-15T10:00:00Z", "contact": {"id": "9"} } ```
#### Update Task
```bash PATCH /keap/crm/rest/v2/tasks/{task_id} Content-Type: application/json
{ "completed": true } ```
#### Delete Task
```bash DELETE /keap/crm/rest/v2/tasks/{task_id} ```
### Opportunity Operations
#### List Opportunities
```bash GET /keap/crm/rest/v2/opportunities ```
#### Get Opportunity
```bash GET /keap/crm/rest/v2/opportunities/{opportunity_id} ```
#### Create Opportunity
```bash POST /keap/crm/rest/v2/opportunities Content-Type: application/json
{ "opportunity_title": "New Deal", "contact": {"id": "9"}, "stage": {"id": "1"}, "estimated_close_date": "2026-03-01" } ```
#### Update Opportunity
```bash PATCH /keap/crm/rest/v2/opportunities/{opportunity_id} Content-Type: application/json
{ "stage": {"id": "2"} } ```
#### Delete Opportunity
```bash DELETE /keap/crm/rest/v2/opportunities/{opportunity_id} ```
#### List Opportunity Stages
```bash GET /keap/crm/rest/v2/opportunities/stages ```
### Order Operations
#### List Orders
```bash GET /keap/crm/rest/v2/orders ```
#### Get Order
```bash GET /keap/crm/rest/v2/orders/{order_id} ```
#### Create Order
```bash POST /keap/crm/rest/v2/orders Content-Type: application/json
{ "contact": {"id": "9"}, "order_date": "2026-02-08", "order_title": "Product Order" } ```
#### Add Order Item
```bash POST /keap/crm/rest/v2/orders/{order_id}/items Content-Type: application/json
{ "product": {"id": "1"}, "quantity": 2 } ```
### Product Operations
#### List Products
```bash GET /keap/crm/rest/v2/products ```
#### Get Product
```bash GET /keap/crm/rest/v2/products/{product_id} ```
#### Create Product
```bash POST /keap/crm/rest/v2/products Content-Type: application/json
{ "product_name": "Consulting Package", "product_price": 500.00, "product_short_description": "1 hour consulting" } ```
### Campaign Operations
#### List Campaigns
```bash GET /keap/crm/rest/v2/campaigns ```
#### Get Campaign
```bash GET /keap/crm/rest/v2/campaigns/{campaign_id} ```
#### List Campaign Sequences
```bash GET /keap/crm/rest/v2/campaigns/{campaign_id}/sequences ```
#### Add Contacts to Sequence
```bash POST /keap/crm/rest/v2/campaigns/{campaign_id}/sequences/{sequence_id}:addContacts Content-Type: application/json
{ "contact_ids": ["1", "2"] } ```
#### Remove Contacts from Sequence
```bash POST /keap/crm/rest/v2/campaigns/{campaign_id}/sequences/{sequence_id}:removeContacts Content-Type: application/json
{ "contact_ids": ["1", "2"] } ```
### Email Operations
#### List Emails
```bash GET /keap/crm/rest/v2/emails ```
#### Get Email
```bash GET /keap/crm/rest/v2/emails/{email_id} ```
#### Send Email
```bash POST /keap/crm/rest/v2/emails:send Content-Type: application/json
{ "contacts": [{"id": "9"}], "subject": "Hello", "html_content": "<p>Email body</p>" } ```
### User Operations
#### List Users
```bash GET /keap/crm/rest/v2/users ```
#### Get User
```bash GET /keap/crm/rest/v2/users/{user_id} ```
### Subscription Operations
#### List Subscriptions
```bash GET /keap/crm/rest/v2/subscriptions ```
#### Get Subscription
```bash GET /keap/crm/rest/v2/subscriptions/{subscription_id} ```
### Affiliate Operations
#### List Affiliates
```bash GET /keap/crm/rest/v2/affiliates ```
#### Get Affiliate
```bash GET /keap/crm/rest/v2/affiliates/{affiliate_id} ```
### Automation Operations
#### List Automations
```bash GET /keap/crm/rest/v2/automations ```
#### Get Automation
```bash GET /keap/crm/rest/v2/automations/{automation_id} ```
## Pagination
Keap uses token-based pagination:
```bash GET /keap/crm/rest/v2/contacts?page_size=50 ```
**Response:** ```json { "contacts": [...], "next_page_token": "abc123" } ```
For subsequent pages, use the `page_token` parameter:
```bash GET /keap/crm/rest/v2/contacts?page_size=50&page_token=abc123 ```
When `next_page_token` is empty, there are no more pages.
## Filtering
Use the `filter` parameter for filtering results:
```bash GET /keap/crm/rest/v2/contacts?filter=given_name==John GET /keap/crm/rest/v2/[email protected] GET /keap/crm/rest/v2/tasks?filter=completed==false ```
## Code Examples
### JavaScript
```javascript const response = await fetch( 'https://gateway.maton.ai/keap/crm/rest/v2/contacts?page_size=10', { headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}` } } ); const data = await response.json(); ```
### Python
```python import os import requests
response = requests.get( 'https://gateway.maton.ai/keap/crm/rest/v2/contacts', headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}, params={'page_size': 10} ) data = response.json() ```
## Notes
- All API paths must include `/crm/rest` prefix (e.g., `/keap/crm/rest/v2/contacts`) - Keap uses v2 REST API (previous v1 API is deprecated) - Timestamps are in ISO 8601 format - IDs are returned as strings - Pagination uses `page_size` and `page_token` (not offset-based) - Maximum `page_size` is 1000 - IMPORTANT: When piping curl output to `jq` or other commands, environment variables like `$MATON_API_KEY` may not expand correctly in some shell environments
## Error Handling
| Status | Meaning | |--------|---------| | 400 | Missing Keap connection or invalid request | | 401 | Invalid or missing Maton API key | | 403 | Not authorized (check OAuth scopes) | | 404 | Resource not found | | 429 | Rate limited | | 4xx/5xx | Passthrough error from Keap API |
### Troubleshooting: API Key Issues
1. Check that the `MATON_API_KEY` environment variable is set:
```bash echo $MATON_API_KEY ```
2. Verify the API key is valid by listing connections:
```bash python <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF ```
### Troubleshooting: Invalid App Name
1. Ensure your URL path starts with `keap`. For example:
- Correct: `https://gateway.maton.ai/keap/crm/rest/v2/contacts` - Incorrect: `https://gateway.maton.ai/crm/rest/v2/contacts`
## Resources
- [Keap Developer Portal](https://developer.infusionsoft.com/) - [Keap REST API V2 Documentation](https://developer.infusionsoft.com/docs/restv2/) - [Getting Started Guide](https://developer.infusionsoft.com/getting-started/) - [OAuth 2.0 Authentication](https://developer.infusionsoft.com/authentication/) - [Maton Community](https://discord.com/invite/dBfFAcefs2) - [Maton Support](mailto:[email protected])