How to Use the Freshdesk API (2026): Auth, API Key & Examples
If you want to pull tickets into a data warehouse, sync contacts from your CRM, fire off a ticket when an order fails, or build an AI agent on top of your help desk, you'll be talking to the Freshdesk API. It's a clean, well-documented REST API — but the first hour is usually spent on the same handful of questions: what's the base URL, where's my API key, how does authentication work, and why am I getting a 429?
This guide answers all of that in order. We'll cover the Freshdesk REST API v2 at a high level, how to find or generate your API key, how Basic Auth works (with a real curl you can paste), the endpoints you'll actually use, pagination, rate limits, error codes, and a couple of copy-paste examples in Python and Node. Details are verified against Freshdesk's developer docs as of June 2026 — Freshworks revises the product periodically, so treat exact limits as "confirm in your account." If you're new to the product itself, start with what is Freshdesk; if you're trying to automate workflows rather than write code, how to automate Freshdesk with AI is the companion piece.
What the Freshdesk REST API v2 is
The Freshdesk API is a RESTful, JSON-over-HTTPS API. You make standard HTTP requests — GET to read, POST to create, PUT to update, DELETE to remove — against resource URLs, and you get JSON back. There's no special SDK required (though community libraries exist); anything that can make an HTTPS request can use it.
The current version is v2, and it's the one you should build against. You'll occasionally find references to a legacy v1 — ignore it for new work; v2 is the documented, supported surface.
Every request goes to your account's own subdomain. The base URL format is:
https://<your-domain>.freshdesk.com/api/v2/
So if your help desk lives at acme.freshdesk.com, your tickets endpoint is https://acme.freshdesk.com/api/v2/tickets. There's no separate API host — the API rides on the same domain as your portal.
How to get your Freshdesk API key
Freshdesk authenticates API calls with a personal API key tied to your agent account. Every agent has their own key, and calls made with it inherit that agent's permissions — a useful thing to remember, because a key from a restricted agent won't see tickets that agent can't see.
To find your key (per Freshdesk's docs):
- Log in to your Freshdesk account.
- Click your profile picture in the top-right corner.
- Select Profile Settings.
- Your API key is shown in the right-hand pane of the profile page.
A few things worth knowing before you build:
- Plan gate. On the oldest Sprout plan, the API key (and API access) isn't available. On current paid plans it is.
- It's a secret. The key grants full API access at that agent's permission level. Treat it like a password — keep it out of client-side code, repos, and logs. Store it in an environment variable or secrets manager.
- Rotation. If a key leaks, you can regenerate it from the same profile screen, which invalidates the old one.
- Service accounts. For an integration that should run independently of any one person, create a dedicated agent for it and use that agent's key, so the integration doesn't break when someone leaves.
Freshdesk API authentication: Basic Auth
Freshdesk uses HTTP Basic Authentication. The twist that trips people up: you put the API key in the username field, and literally any non-empty string as the password (the convention is the letter X). The password is ignored when a valid API key is supplied.
In curl, the -u flag handles Basic Auth for you:
curl -v -u YOUR_API_KEY:X \
-H "Content-Type: application/json" \
-X GET 'https://your-domain.freshdesk.com/api/v2/tickets'
Under the hood, Basic Auth Base64-encodes username:password and sends it as an Authorization header. If your HTTP client doesn't have a Basic Auth helper, you build that header yourself — Base64-encode YOUR_API_KEY:X and send:
Authorization: Basic <base64 of "YOUR_API_KEY:X">
That's the whole authentication story. No OAuth dance is required for the standard API; the key is the credential. (Freshdesk does offer OAuth for certain marketplace-app scenarios, but for server-to-server scripting, the API key is what you want.)
The endpoints you'll actually use
Freshdesk exposes a resource for nearly every object in the product. The ones you'll reach for most:
- Tickets —
/api/v2/tickets. Create, read, update, delete, and list tickets. The center of gravity for almost every integration. - Conversations —
/api/v2/tickets/{id}/replyand/api/v2/tickets/{id}/notes. A conversation is a reply (sent to the customer) or a note (internal). This is how you post a response programmatically. - Contacts —
/api/v2/contacts. The customers/requesters. Create and sync them from your own systems. - Agents —
/api/v2/agents. Your support team members — handy for routing logic and reporting. - Companies —
/api/v2/companies, and Groups —/api/v2/groups, round out the common set for account-level reporting and assignment.
A quick map of HTTP verbs to actions:
| Action | Method | Example endpoint |
|---|---|---|
| List tickets | GET | /api/v2/tickets |
| Get one ticket | GET | /api/v2/tickets/{id} |
| Create a ticket | POST | /api/v2/tickets |
| Update a ticket | PUT | /api/v2/tickets/{id} |
| Reply to a ticket | POST | /api/v2/tickets/{id}/reply |
| Add a private note | POST | /api/v2/tickets/{id}/notes |
| List contacts | GET | /api/v2/contacts |
A note on Freshdesk's coded fields
Several ticket fields are sent as integers, not words, which surprises first-time users. When you create or read a ticket:
- Status:
2= Open,3= Pending,4= Resolved,5= Closed. - Priority:
1= Low,2= Medium,3= High,4= Urgent.
So a "priority": 4 in your JSON means Urgent. Keep this table handy — it's the source of a lot of "why did my ticket come in as Low?" confusion.
Example: create a ticket with curl
To create a ticket you must supply at least one way to identify the requester — requester_id (an existing contact), or email (Freshdesk creates the contact if it doesn't exist), or one of phone, twitter_id, facebook_id, or unique_external_id. subject and description are required for a meaningful ticket; status defaults to Open and priority to Low if you omit them.
curl -v -u YOUR_API_KEY:X \
-H "Content-Type: application/json" \
-X POST 'https://your-domain.freshdesk.com/api/v2/tickets' \
-d '{
"subject": "Payment failed at checkout",
"description": "Customer reports a 502 when paying with card.",
"email": "[email protected]",
"priority": 2,
"status": 2
}'
A successful create returns HTTP 201 and the full ticket object as JSON, including the new id you'll use for follow-up calls.
Example: list and read tickets (Python)
Here's a minimal, dependency-light Python script using requests. It reads the key from an environment variable (never hard-code it) and fetches a page of tickets:
import os
import requests
DOMAIN = "your-domain" # the part before .freshdesk.com
API_KEY = os.environ["FRESHDESK_API_KEY"]
BASE = f"https://{DOMAIN}.freshdesk.com/api/v2"
# Basic Auth: API key as username, any string ("X") as password
auth = (API_KEY, "X")
resp = requests.get(f"{BASE}/tickets", auth=auth, params={"per_page": 100})
resp.raise_for_status()
tickets = resp.json()
print(f"Fetched {len(tickets)} tickets")
for t in tickets:
print(t["id"], t["subject"], "status:", t["status"])
Example: create a ticket (Node)
The same idea in Node using the built-in fetch (Node 18+). Basic Auth is just a Base64-encoded Authorization header:
const DOMAIN = "your-domain";
const API_KEY = process.env.FRESHDESK_API_KEY;
const BASE = `https://${DOMAIN}.freshdesk.com/api/v2`;
const authHeader = "Basic " + Buffer.from(`${API_KEY}:X`).toString("base64");
const res = await fetch(`${BASE}/tickets`, {
method: "POST",
headers: {
"Authorization": authHeader,
"Content-Type": "application/json",
},
body: JSON.stringify({
subject: "API test ticket",
description: "Created from Node.",
email: "[email protected]",
priority: 1,
}),
});
if (!res.ok) {
throw new Error(`Freshdesk error ${res.status}: ${await res.text()}`);
}
const ticket = await res.json();
console.log("Created ticket", ticket.id);
Pagination
List endpoints don't return everything at once. By default the API returns 30 objects per page, and you can raise that to a maximum of 100 with the per_page parameter. To walk through results, increment the page parameter — but the cleaner approach is to follow the link response header, which Freshdesk populates with the URL of the next page when one exists.
curl -v -u YOUR_API_KEY:X \
'https://your-domain.freshdesk.com/api/v2/tickets?per_page=100&page=1' -D -
The -D - dumps response headers; look for a link: header like <https://your-domain.freshdesk.com/api/v2/tickets?per_page=100&page=2>; rel="next". When there's no link header, you've hit the last page.
One important exception: the Filter / Search Tickets endpoint (/api/v2/search/tickets) behaves differently — it returns 30 results per page and ignores per_page, so don't try to set page size there. (Freshdesk community confirms this.)
Rate limits
Freshdesk enforces a per-minute rate limit applied to your whole account — not per agent, not per IP. The exact ceiling depends on your plan. As a rough guide (Freshworks has changed these over time, so confirm against the official article and your own headers):
| Plan | Approx. requests / minute |
|---|---|
| Trial | ~50 |
| Free / Growth | ~100 / 200 |
| Pro | ~400 |
| Enterprise | ~700 |
There are also tighter per-endpoint sub-limits on some heavy operations, so a single endpoint can throttle before your account-wide budget is gone.
Don't guess your remaining budget — read it from the response headers present on every call:
X-Ratelimit-Total— your limit for the window.X-Ratelimit-Remaining— how many calls you have left.X-Ratelimit-Used-CurrentRequest— what the current request cost.
When you exceed the limit, Freshdesk returns HTTP 429 along with a Retry-After header telling you how many seconds to wait. The correct pattern is to back off for exactly that long, then retry — ideally with exponential backoff and jitter if you're running many workers. Build this in from day one; it's the single most common thing that breaks naive integrations at scale.
Error codes
API errors come back with a standard HTTP status and a JSON body describing what went wrong. The ones you'll see most (full table in Freshdesk's docs):
| Code | Meaning | Usual cause |
|---|---|---|
200 / 201 | Success | 200 for reads/updates, 201 for a create |
400 | Bad Request | Malformed JSON, missing required field, or a value out of range |
401 | Unauthorized | Wrong/missing API key, or bad Basic Auth header |
403 | Access Denied | Valid key, but that agent lacks permission for the action |
404 | Not Found | Wrong ID, wrong domain in the URL, or a typo'd endpoint |
429 | Rate Limited | Too many calls — honor the Retry-After header |
When you hit a 400, read the response body: Freshdesk returns a description plus a per-field errors array (field, message, code) that usually tells you exactly which attribute it rejected. That detail saves a lot of guessing.
When you'd reach for an AI layer instead of the raw API
The API is the right tool when you want deterministic, programmatic control — syncing data, wiring Freshdesk into your own stack, or triggering tickets from other systems. But a growing share of "API projects" are really "I want tickets to be triaged and answered automatically," and that's a different shape of problem. Writing and maintaining the scripts — classify the ticket, look up the customer, draft a reply, decide whether it's safe to send — is a real engineering commitment, and it's brittle as your product and policies change.
That's the gap an AI agent layer like Macha fills. Macha isn't a help desk and isn't a Freshdesk replacement — it runs on top of your existing Freshdesk, using the same API plus your connected knowledge base to triage, draft, and resolve routine tickets without you scripting the logic by hand. Anything it can't confidently handle stays a normal ticket for a human, with full context attached. (We built this on Zendesk first and the same model applies to Freshdesk — see Macha on Zendesk.) The honest trade-off: it's another integration to configure, and it's only as good as the knowledge you give it; billing is per AI action — each automated step it takes — not per closed ticket. If your goal is "resolve more tickets" rather than "move data around," that's the line where hand-rolled API scripts stop being the cheapest path. You can try it free — 7-day free trial, no credit card required. For the no-code automation angle specifically, see how to automate Freshdesk with AI.
Frequently asked questions
What is the Freshdesk API base URL? It's your own subdomain plus the v2 path: https://<your-domain>.freshdesk.com/api/v2/. For example, an account at acme.freshdesk.com reaches tickets at https://acme.freshdesk.com/api/v2/tickets. There is no separate API host.
Where do I find my Freshdesk API key? Click your profile picture in the top-right, choose Profile Settings, and your API key is shown in the right-hand pane. Each agent has their own key, and it inherits that agent's permissions. The API key isn't available on the legacy Sprout plan.
How does Freshdesk API authentication work? With HTTP Basic Auth. Put your API key in the username field and any non-empty string (conventionally X) as the password. In curl that's -u YOUR_API_KEY:X. The credentials are Base64-encoded into an Authorization: Basic ... header.
What are the Freshdesk API rate limits? A per-minute limit applied account-wide, varying by plan — roughly 50/min on trial up to ~700/min on Enterprise, with tighter per-endpoint sub-limits. Read X-Ratelimit-Remaining from response headers, and when you get a 429, wait for the number of seconds in the Retry-After header before retrying. Confirm exact figures in Freshdesk's docs and your own account.
How do I paginate Freshdesk API results? List endpoints return 30 items per page by default; set per_page up to 100 and increment page, or simply follow the link response header to the next page. Note that the Filter/Search Tickets endpoint always returns 30 per page and ignores per_page.
Why am I getting a 400 error creating a ticket? Usually a missing requester identifier or a bad field value. You must supply at least one of requester_id, email, phone, twitter_id, facebook_id, or unique_external_id. Read the errors array in the response body — it names the exact field and reason.
The bottom line
The Freshdesk REST API v2 is straightforward once the basics click: every call goes to https://<your-domain>.freshdesk.com/api/v2/, you authenticate with Basic Auth using your API key as the username and X as the password, and you work with JSON resources for tickets, contacts, conversations, and agents. The two things that catch everyone are the integer-coded fields (status and priority are numbers) and the rate limits — so keep the code tables handy and build 429/Retry-After handling in from the start. With those in place, paginate via the link header, read errors from the response body, and you can sync, create, and update almost anything in your help desk. From here, see what Freshdesk is for the product overview, or how to automate Freshdesk with AI if your real goal is resolving tickets, not just moving data.
Freshdesk REST API v2 details verified against Freshdesk's developer and support documentation, June 2026. Freshworks updates its API and rate limits periodically — confirm endpoints, limits, and field values in the live docs and your own account before relying on them.
Add AI agents to your Freshdesk
Macha resolves tickets end to end on Freshdesk — no migration, no code.
Zendesk
Freshdesk
Gorgias
Front
Shopify
Stripe
Slack
Notion
Google Workspace
Confluence

