Summary and recommendation
The SendGrid v3 API exposes Teammate and Subuser management under https://api.sendgrid.com/v3, authenticated via Bearer token in the Authorization header.
Teammate operations require explicit scope grants on the calling key: teammates.read, teammates.create, teammates.update, and teammates.delete are each enforced independently
a restricted key missing any one of these will receive HTTP 403 regardless of account-level permissions.
Teammates and Subusers are architecturally distinct: Teammates are collaborators scoped into one account;
Subusers are isolated child accounts with their own API keys, sending stats, and suppression lists.
For teams building identity graph pipelines or syncing SendGrid access state into a directory, this distinction must be modeled explicitly - a unified user list does not exist in the API.
Rate limits are 600 requests per minute across general v3 endpoints.
The API returns X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers;
HTTP 429 signals exhaustion.
A Retry-After header is not documented.
Pagination uses limit and offset params with a default page size of 10 and a maximum of 500.
API quick reference
| Has user API | Yes |
| Auth method | API Key (Bearer token in Authorization header) |
| Base URL | Official docs |
| SCIM available | No |
| SCIM plan required | Private Beta via Twilio Organizations — contact account executive |
Authentication
Auth method: API Key (Bearer token in Authorization header)
Setup steps
- Log in to the SendGrid dashboard and navigate to Settings > API Keys.
- Click 'Create API Key', assign a name, and select Full Access or restricted scopes.
- Copy the generated API key immediately (shown only once).
- Include the key in all requests as: Authorization: Bearer
Required scopes
| Scope | Description | Required for |
|---|---|---|
| user.profile.read | Read the authenticated account's profile information. | GET /user/profile |
| user.profile.update | Update the authenticated account's profile. | PUT /user/profile |
| teammates.read | List and retrieve Teammate details. | GET /teammates, GET /teammates/{username} |
| teammates.create | Invite new Teammates to the account. | POST /teammates |
| teammates.update | Update Teammate permissions. | PATCH /teammates/{username} |
| teammates.delete | Remove a Teammate from the account. | DELETE /teammates/{username} |
| subusers.create | Create new Subuser accounts. | POST /subusers |
| subusers.read | List and retrieve Subuser details. | GET /subusers |
| subusers.update | Update Subuser properties (e.g., disable/enable). | PATCH /subusers/{subuser_name} |
| subusers.delete | Delete a Subuser account. | DELETE /subusers/{subuser_name} |
User object / data model
| Field | Type | Description | On create | On update | Notes |
|---|---|---|---|---|---|
| username | string | Unique username for the account or Teammate. | required | immutable | Used as identifier in URL paths for Teammates and Subusers. |
| string | Email address of the user or Teammate. | required | optional | For Teammates, this is the invite destination. | |
| first_name | string | First name of the user. | optional | optional | Part of /user/profile for the account owner. |
| last_name | string | Last name of the user. | optional | optional | Part of /user/profile for the account owner. |
| is_admin | boolean | Whether the Teammate has admin-level access. | required | optional | If true, scopes array is ignored; full access is granted. |
| scopes | array[string] | List of permission scopes assigned to a Teammate. | required (if is_admin=false) | optional | Ignored when is_admin=true. |
| user_type | string | Type of Teammate: 'admin', 'owner', or 'teammate'. | system-assigned | read-only | Returned in GET responses; not settable directly. |
| address | string | Street address on the account profile. | optional | optional | Part of /user/profile. |
| city | string | City on the account profile. | optional | optional | Part of /user/profile. |
| country | string | Country on the account profile. | optional | optional | Part of /user/profile. |
| phone | string | Phone number on the account profile. | optional | optional | Part of /user/profile. |
| disabled | boolean | Whether the Subuser account is disabled. | optional (default false) | optional | Subuser-specific field; set via PATCH /subusers/{subuser_name}. |
| ips | array[string] | IP addresses assigned to a Subuser. | required for Subusers | optional | Must be IPs already allocated to the parent account. |
| password | string | Password for a new Subuser account. | required for Subusers | not updatable via this API | Write-only; never returned in responses. |
Core endpoints
Get account profile
- Method: GET
- URL:
https://api.sendgrid.com/v3/user/profile - Watch out for: Returns the authenticated API key owner's profile, not arbitrary users.
Request example
GET /v3/user/profile
Authorization: Bearer <API_KEY>
Response example
{
"address": "123 Main St",
"city": "Denver",
"country": "US",
"email": "owner@example.com",
"first_name": "Jane",
"last_name": "Doe"
}
List Teammates
- Method: GET
- URL:
https://api.sendgrid.com/v3/teammates - Watch out for: Only returns Teammates for the account associated with the API key; does not include Subusers.
Request example
GET /v3/teammates?limit=10&offset=0
Authorization: Bearer <API_KEY>
Response example
{
"result": [
{"username": "jdoe", "email": "jdoe@example.com",
"is_admin": false, "user_type": "teammate"}
]
}
Invite Teammate
- Method: POST
- URL:
https://api.sendgrid.com/v3/teammates - Watch out for: Creates a pending invite, not an active user. The invitee must accept via email before they appear as an active Teammate.
Request example
POST /v3/teammates
Authorization: Bearer <API_KEY>
{
"email": "newuser@example.com",
"scopes": ["mail.send"],
"is_admin": false
}
Response example
{
"token": "abc123invite",
"email": "newuser@example.com",
"scopes": ["mail.send"],
"is_admin": false
}
Update Teammate permissions
- Method: PATCH
- URL:
https://api.sendgrid.com/v3/teammates/{username} - Watch out for: Cannot update the account owner's permissions via this endpoint.
Request example
PATCH /v3/teammates/jdoe
Authorization: Bearer <API_KEY>
{
"scopes": ["mail.send", "stats.read"],
"is_admin": false
}
Response example
{
"username": "jdoe",
"is_admin": false,
"scopes": ["mail.send", "stats.read"]
}
Delete Teammate
- Method: DELETE
- URL:
https://api.sendgrid.com/v3/teammates/{username} - Watch out for: Permanent and immediate. Cannot delete the account owner.
Request example
DELETE /v3/teammates/jdoe
Authorization: Bearer <API_KEY>
Response example
HTTP 204 No Content
Create Subuser
- Method: POST
- URL:
https://api.sendgrid.com/v3/subusers - Watch out for: IPs must already be provisioned to the parent account. Subusers are isolated sending accounts, not collaborators.
Request example
POST /v3/subusers
Authorization: Bearer <API_KEY>
{
"username": "subuser1",
"email": "sub@example.com",
"password": "SecurePass1!",
"ips": ["1.2.3.4"]
}
Response example
{
"username": "subuser1",
"email": "sub@example.com",
"disabled": false,
"ips": ["1.2.3.4"]
}
List Subusers
- Method: GET
- URL:
https://api.sendgrid.com/v3/subusers - Watch out for: Returns a flat array (not wrapped in a result key). Max 500 per page.
Request example
GET /v3/subusers?limit=10&offset=0
Authorization: Bearer <API_KEY>
Response example
[
{"username": "subuser1", "email": "sub@example.com",
"disabled": false, "id": 12345}
]
Enable/Disable Subuser
- Method: PATCH
- URL:
https://api.sendgrid.com/v3/subusers/{subuser_name} - Watch out for: Only the 'disabled' boolean can be updated via this endpoint. Other Subuser properties require separate endpoints.
Request example
PATCH /v3/subusers/subuser1
Authorization: Bearer <API_KEY>
{"disabled": true}
Response example
HTTP 204 No Content
Rate limits, pagination, and events
- Rate limits: SendGrid enforces rate limits per API key. The general limit is 600 requests per minute for most v3 endpoints. Some endpoints have lower per-endpoint limits documented individually.
- Rate-limit headers: Yes
- Retry-After header: No
- Rate-limit notes: When rate limited, the API returns HTTP 429. The docs reference X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Retry-After header behavior is not explicitly documented.
- Pagination method: offset
- Default page size: 10
- Max page size: 500
- Pagination pointer: limit / offset
| Plan | Limit | Concurrent |
|---|---|---|
| All plans (general v3 endpoints) | 600 requests/minute | 0 |
- Webhooks available: No
- Webhook notes: SendGrid does not offer webhooks for user-management events (Teammate invites, Subuser changes). The Event Webhook covers email delivery events only.
- Alternative event strategy: Poll the Teammates and Subusers list endpoints periodically to detect changes.
SCIM API status
- SCIM available: No
- SCIM version: Not documented
- Plan required: Private Beta via Twilio Organizations - contact account executive
- Endpoint: Not documented
Limitations:
- SCIM provisioning is in Private Beta as part of the Twilio Organizations feature and is not generally available.
- No public SCIM endpoint is documented; access requires direct engagement with a Twilio/SendGrid account executive.
- JIT (Just-in-Time) provisioning via SAML SSO is the current generally available alternative for automated user provisioning.
- SAML SSO (prerequisite for JIT) is available on Pro and Premier plans.
Common scenarios
Onboarding a Teammate via API creates a pending invite, not an active user.
POST /v3/teammates with is_admin set to false and a scopes array sends the invitation email;
the Teammate does not appear as active until they accept.
There is no webhook for acceptance events - poll GET /v3/teammates to detect status change and keep your identity graph current.
Deprovisioning requires two separate operations: DELETE /v3/teammates/{username} revokes UI and API access immediately, but API keys the Teammate created under the account remain live.
A complete deprovision flow must follow with GET /v3/api_keys to audit and DELETE /v3/api_keys/{api_key_id} to revoke orphaned keys.
Omitting this step leaves credentials active after the Teammate record is gone.
Subuser lifecycle management supports a soft-disable path: PATCH /v3/subusers/{subuser_name} with {"disabled": true} suspends sending without destroying data.
Hard deletion via DELETE /v3/subusers/{subuser_name} is irreversible and removes all associated history and suppression lists.
Always disable before deleting if data retention is a requirement.
Subuser creation requires pre-allocated IPs on the parent account;
assigning an unprovisioned IP returns an error.
Onboard a new Teammate with restricted permissions
- Create a restricted API key with 'teammates.create' scope.
- POST /v3/teammates with the new user's email, is_admin=false, and desired scopes array.
- Inform the user to check their email and accept the invitation.
- After acceptance, verify with GET /v3/teammates/{username} that user_type is 'teammate' and scopes are correct.
Watch out for: The Teammate is not active until the invite is accepted. There is no webhook to notify you of acceptance; poll GET /v3/teammates to detect status change.
Deprovision a departing Teammate
- Identify the Teammate's username via GET /v3/teammates.
- DELETE /v3/teammates/{username} to immediately revoke access.
- Audit and rotate any API keys the Teammate may have created under the account (managed separately via GET /v3/api_keys).
Watch out for: Deleting a Teammate does not automatically invalidate API keys they created. API keys must be audited and deleted separately.
Create and disable a Subuser for a client
- Ensure the parent account has at least one IP allocated (check SendGrid dashboard).
- POST /v3/subusers with username, email, password, and ips array.
- When the client relationship ends, PATCH /v3/subusers/{subuser_name} with {"disabled": true} to suspend sending without deleting data.
- To permanently remove, DELETE /v3/subusers/{subuser_name}.
Watch out for: Subuser deletion is irreversible and removes all associated sending history and settings. Disable first if data retention is needed.
Why building this yourself is a trap
SCIM provisioning is not publicly available. It exists in Private Beta as part of the Twilio Organizations feature and requires direct engagement with a Twilio/SendGrid account executive - there is no public SCIM endpoint to target. JIT provisioning via SAML SSO is the currently available automated provisioning path, gated to Pro and Premier plans.
The absence of user-management webhooks means any system maintaining an identity graph against SendGrid - tracking who has access, at what scope, and whether they are active - must rely entirely on polling. There is no event stream for Teammate invites, acceptances, deletions, or permission changes. Build polling intervals and idempotent reconciliation logic accordingly.
The account owner record cannot be deleted or have permissions modified via the Teammates API. Any automation that iterates over all Teammates and attempts bulk permission updates must explicitly skip records where user_type is owner to avoid errors.
Automate SendGrid workflows without one-off scripts
Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.