Freshdesk API v2

Freshdesk API v2 is the only version you should write against. It's cleaner than v1 in ways that matter, and it has three quirks that cost a morning each.

What Freshdesk API v2 changed

v1 was a helpdesk API that grew organically. v2 was a deliberate redesign, and the differences are structural rather than cosmetic.

JSON throughout. v1 offered XML alongside JSON and used .json suffixes on paths. v2 is JSON only, with clean resource paths.
Honest HTTP semantics. Proper status codes, proper verbs, 201 on create, 204 where nothing comes back. v1 was looser about all of it.
Consistent naming. Snake case field names, consistent identifiers, and payloads that are not wrapped in an outer object the way many older APIs are. You post the ticket fields at the top level.
Structured errors. A predictable envelope with a code, a description and, for validation problems, a per-field breakdown naming exactly which attribute failed and why.
Embedded resources via `include`. One request can return a ticket plus its requester and its stats, instead of three round trips.
Real rate limit headers. You can see your remaining budget and the cost of the call you just made.

If you're maintaining something written against v1, the migration is not a rename exercise. Field names, response shapes and pagination all differ, so plan it as a rewrite of the transport layer with your business logic kept intact.

Versioning in practice

The version is in the path: /api/v2/. There is no version header, no content negotiation, no date-pinned version like some vendors use.

This has a practical implication people miss. Because there's no way to pin a minor version, additive changes land in the API you are already calling. New fields appear in responses. New optional parameters become available. Nothing announces it in your code.

So write a tolerant client. Ignore unknown fields rather than deserialising into a strict model that throws on anything new. Never assume the key order in a JSON object, and never assume a response contains only the keys documented on the day you wrote the parser.

Watch the Freshworks developer changelog if the integration is load bearing. That's the only place breaking changes and deprecations get announced with any lead time.

Pagination, and where it stops

List endpoints paginate with page and per_page query parameters. The default page size is small, and there is a documented maximum you should confirm against the current reference rather than trusting from memory. One hundred is the figure most list endpoints use.

Two behaviours matter more than the numbers.

Do not count pages, follow links. Paginated responses carry a Link header pointing at the next page. When there is no next link, you're done. Deciding you are finished because a page came back short is a bug that shows up only when a page boundary lands exactly on your total.

Deep pagination is capped. List endpoints stop giving you results beyond a certain depth, and search stops far sooner. You can't walk a hundred thousand tickets page by page from the top. The way round it is to slice by time, using a filter such as updated_since to take a window at a time, then move the window. Every large export ends up built this way.

And remember the queue is moving while you page. Tickets created mid-crawl shift positions. For anything you need to reconcile, sort and slice by an immutable window rather than by whatever the default ordering happens to be.

The include parameter

This is the best thing in v2 and the easiest to overuse.

On a single ticket you can ask for the conversation thread, the requester, the company and the stats in one call. On a ticket list you can ask for a smaller set, typically the requester, the stats and the description, because the full conversation on a hundred tickets would be enormous.

The trade-off is credits. An included resource makes the request cost more against your hourly budget, so a naive "include everything on every list call" burns your allowance several times faster than the plain version for data you then discard.

The rule I use: include what you'll read on this pass, nothing else. If you only need requester email addresses, include the requester. If you need the description on a list, include it rather than looping back for each ticket individually, which is far worse.

The gotchas

Status and priority are integers. Open, Pending, Resolved and Closed map to 2, 3, 4 and 5. Priority runs 1 for Low through 4 for Urgent. Custom statuses get their own numbers, so read them from the API rather than hardcoding a lookup table you copied from a forum.
Custom fields are namespaced and account-specific. They arrive under a custom fields object with machine names, not the labels you see in the interface. Discover them from the ticket fields endpoint at startup.
Update is a partial update. Send only the attributes you want changed. Sending the whole object back is how people accidentally overwrite a field a colleague edited thirty seconds earlier.
Deleted and spam tickets are hidden by default from list results, which quietly explains most count mismatches against the interface.
Timestamps are UTC. Your agents see local time, your API does not. Every "the numbers are off by an hour" bug report is this.
Attachments need multipart. They do not go in the JSON body, which means one code path in your client that looks unlike all the others.
FAQ

Frequently asked questions

What changed from v1, and how does pagination work?

Freshdesk API version 2 restructured the resources and standardised errors. Freshdesk API pagination is page and per_page with a hard ceiling, the Freshdesk API include parameter fetches related records in the same call, and a Freshdesk API migration from v1 is mostly path and payload changes.

Is v1 still available alongside Freshdesk API v2?

Treat v1 as legacy and build nothing new against it. v2 is the documented, supported version and the only one worth targeting.

How many results can I get per page?

List endpoints commonly cap at 100 per page with a smaller default, and search is tighter still. Confirm the current figures in the API reference, because they differ per endpoint.

How do I export more tickets than pagination allows?

Slice by time. Filter on a date window such as updated_since, page through that window, then advance it. Deep pagination is capped, so windowing is the only reliable approach at scale.

What does the include parameter cost?

Extra rate limit credits per request. It's still far cheaper than a follow-up call per record, so include what you'll actually read and nothing more.

Why does my ticket count not match the interface?

Deleted and spam tickets are excluded from API list results by default, and your filters or time window may differ from the view you're comparing against.

A cleaner queue to sync

Every duplicate ticket is another record your integration pages through. Ticket Merger removes them at source, automatically, on Freshdesk and Zendesk.

Start free trial

14-day free trial. No credit card required.