Integration Use Case

Pagination, Search, Sorting and Filtering in the Hipcall API

Pagination with limit/offset, q search, sort ordering and bracket filters in the Hipcall API, with measured notes on boundary values and error responses.

API

Part A: Pagination

A1. Calling with no parameters

Command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

When the request is sent with no pagination parameters, it returns 10 records by default. The top-level structure of the response is a JSON object containing a data and a meta field.

Full response body (phone numbers masked):

{
  "data": [
    {
      "caller_type": "user",
      "missing_call_reason": null,
      "first_touch_duration": 3,
      "ended_at": "2026-09-15T11:18:09Z",
      "uuid": "ff111433-dd1d-4d61-abd3-4b06fa9cd43f",
      "started_at": "2026-09-15T11:17:56Z",
      "user_id": 4200,
      "number_id": 938,
      "caller_id": 4200,
      "direction": "outbound",
      "voicemail_id": null,
      "answered_at": null,
      "company_id": null,
      "missing_call": false,
      "call_duration": 0,
      "callee_type": "contact",
      "callback_user_id": null,
      "callee_id": null,
      "voicemail_url": null,
      "callback_cdr_uuid": null,
      "bridged_at": "2026-09-15T11:17:59Z",
      "team_touch_at": null,
      "call_flow": [
        {
          "action": "hangup",
          "detail": {
            "hangup_by": "user"
          },
          "timestamp": 1789471089
        },
        {
          "action": "bridge",
          "detail": {
            "id": 4200,
            "type": "user"
          },
          "timestamp": 1789471079
        },
        {
          "action": "init",
          "detail": {
            "id": null,
            "type": "contact"
          },
          "timestamp": 1789471076
        }
      ],
      "callback_time": null,
      "callee_number": "+90XXXXXXXXXX",
      "voicemail_type": null,
      "channel_id": 938,
      "hangup_by": "user",
      "credited": false,
      "caller_number": "+90XXXXXXXXXX",
      "record_url": null,
      "channel_type": "number",
      "contact_id": null
    },
  ],
  "meta": {
    "count": 34,
    "offset": 0,
    "limit": 10
  }
}

Note: the returned body (the data array) is very long, so only the first call record is shown here for readability, and phone numbers are masked for security.


A2. What is there besides data?

Besides data, the response contains a meta object.

Fields in the meta object:

FieldDescription
countTotal number of records matching the query and the applied filters.
offsetWhere the listing starts, i.e. how many records were skipped.
limitNumber of records returned in a single request.

In this call’s response, count was 34, offset was 0 and limit was 10.


A3. Upper bound of the limit parameter

The upper bound of limit was found to be 100. When limit=101 is sent, the API rejects the request.

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?limit=101" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "limit": [
      "101 is larger than inclusive maximum 100"
    ]
  }
}

Rather than clamping the excess value down to 100, the API rejects the request with an error.


A4. limit=0 and limit=-5

limit=0 test:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?limit=0" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "limit": [
      "0 is smaller than inclusive minimum 1"
    ]
  }
}

limit=-5 test:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?limit=-5" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "limit": [
      "-5 is smaller than inclusive minimum 1"
    ]
  }
}

In both cases the API rejects the request. Based on these tests, limit cannot be smaller than 1.


A5. Fetching the second page with offset

Since limit defaults to 10 on the first request, offset=10 must be used to get the second page. offset=2 does not mean “second page”; it only skips the first two records and starts the listing at the third.

Command for the second page:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?offset=10" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

A6. Total record count and page arithmetic

The total record count is read from the count field of the meta object.

In this test:

meta.count = 34
meta.limit = 10

The total number of pages is found by dividing the total record count by the page size and rounding up.

Total Pages = ceil(meta.count / meta.limit)
Total Pages = ceil(34 / 10)
Total Pages = 4

Formula:

Total Pages = ceil(meta.count / meta.limit)

On the C# side:

Math.Ceiling((double)meta.count / meta.limit)

So for 34 records with 10 records per page, a total of 4 pages must be fetched.


Part B: Search and sorting

B1. Searching with the q parameter

On /api/v3/contacts, search works both by name and by a fragment of a phone number.

Name search (q=Bill):

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/contacts?q=Bill" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "data": [
    {
      "id": 190987,
      "user": {
        "id": 4200,
        "email": "ornek@firma.com",
        "full_name": "Ahmet Y.",
        "first_name": "Ahmet",
        "last_name": "Y."
      },
      "source": null,
      "external_id": null,
      "full_name": "Bill Gates",
      "first_name": "Bill",
      "last_name": "Gates",
      "company": {
        "id": 80684,
        "name": "Ornek Firma",
        "linkedin_url": "https://www.linkedin.com/search/...",
        "website_url": "https://ornek.firma.com/"
      },
      "custom_url": "https://custom.com",
      "linkedin_url": "https://linkedin.com/in/williamhgates",
      "emails": [
        {
          "order": 1,
          "email": "ornek@firma.com"
        }
      ],
      "life_cycle": null,
      "phones": [
        {
          "id": 246084,
          "number": "+1XXXXXXXXXX",
          "order": 1,
          "country": "US"
        }
      ],
      "sector": null,
      "job_title": "Co-founder",
      "custom_fields": {}
    }
  ],
  "meta": {
    "count": 1,
    "offset": 0,
    "limit": 10
  }
}

Phone number fragment search (q=2964):

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/contacts?q=2964" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output summary:

{
  "data": [
    {
      "id": 190953,
      "user": {
        "id": 4200,
        "email": "ornek@firma.com",
        "full_name": "Ahmet Y.",
        "first_name": "Ahmet",
        "last_name": "Y."
      },
      "source": null,
      "external_id": null,
      "full_name": "Ahmet Y.",
      "first_name": "Ahmet",
      "last_name": "Y.",
      "company": {
        "id": 80684,
        "name": "Ornek Firma",
        "linkedin_url": "https://www.linkedin.com/search/...",
        "website_url": "https://ornek.firma.com/"
      },
      "custom_url": "https://www.linkedin.com/search/...",
      "linkedin_url": "https://www.linkedin.com/search/...",
      "emails": [
        {
          "order": 1,
          "email": "ornek@firma.com"
        }
      ],
      "life_cycle": null,
      "phones": [
        {
          "id": 246052,
          "number": "+90XXXXXXXXXX",
          "order": 1,
          "country": "TR"
        }
      ],
      "sector": null,
      "job_title": "fabsava",
      "custom_fields": {}
    }
  ],
  "meta": {
    "count": 8,
    "offset": 0,
    "limit": 10
  }
}

The phone number fragment search returned 8 matching records.

B2. Behaviour of q when nothing matches

When a search matches no records, the API does not return 404 Not Found. It returns a successful response instead, with an empty data array and meta.count set to 0.

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/contacts?q=xyz123abc" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "data": [],
  "meta": {
    "count": 0,
    "offset": 0,
    "limit": 10
  }
}

B3. The sort parameter

The sort parameter is used by placing a dot (.) between the field to sort on and the sort direction.

Example:

sort=id.asc

Use asc for ascending and desc for descending order.

If more than one sort field is needed, the fields are separated with a comma (,). Variants that control the position of null values, such as asc_nulls_last and desc_nulls_first, are also supported.

B4. Sort fields per endpoint

Sort fields vary by endpoint.

EndpointSupported sort fields
/api/v3/callsstarted_at
/api/v3/contactsid, first_name, last_name, full_name

For example, trying to sort /api/v3/calls by the unsupported user_id field makes the API reject the request:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?offset=2&sort=user_id.asc" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "sort": [
      "Invalid sort fields: [\"user_id.asc\"]"
    ]
  }
}

Using an unsupported sort field on /api/v3/contacts produces the same 422 Unprocessable Entity error and Invalid sort fields message.

B5. An unsupported sort field

When an unsupported sort field is sent, the API rejects the request and returns 422 Unprocessable Entity.

Example:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/contacts?sort=telefon_numarasi.asc" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Response:

{
  "errors": {
    "sort": [
      "Invalid sort fields: [\"telefon_numarasi.asc\"]"
    ]
  }
}

B6. Default sorting

When no sort parameter is sent, endpoints do not all use the same default ordering.

/api/v3/contacts results are returned in id.asc order by default.

/api/v3/calls results are returned in descending order of started_at by default, i.e. newest calls first.

Part C: Filtering

C1. Date range filter

On the /api/v3/calls endpoint, dates are filtered with the gte (greater than or equal) and lte (less than or equal) operators on the started_at field.

The eq operator is not supported on started_at and returns an Unexpected field: eq error when used.

Date range test:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?started_at%5Bgte%5D=2026-09-17T00%3A00%3A00Z&started_at%5Blte%5D=2026-09-17T16%3A00%3A00Z" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "data": [
    {
      "caller_type": "user",
      "missing_call_reason": null,
      "first_touch_duration": 9,
      "ended_at": "2026-09-17T10:43:40Z",
      "uuid": "54cb143f-11d9-4a81-9eaf-5ada7b953e19",
      "started_at": "2026-09-17T10:43:30Z",
      "user_id": 4200,
      "number_id": 939,
      "caller_id": 4200,
      "direction": "outbound",
      "voicemail_id": null,
      "answered_at": "2026-09-17T10:43:39Z",
      "company_id": 80681,
      "missing_call": false,
      "call_duration": 10,
      "callee_type": "contact",
      "callback_user_id": null,
      "callee_id": null,
      "voicemail_url": null,
      "callback_cdr_uuid": null,
      "bridged_at": "2026-09-17T10:43:39Z",
      "team_touch_at": null,
      "call_flow": [
        {
          "action": "hangup",
          "detail": {
            "hangup_by": "contact"
          },
          "timestamp": 1789641820
        },
        {
          "action": "bridge",
          "detail": {
            "id": 4200,
            "type": "user"
          },
          "timestamp": 1789641811
        },
        {
          "action": "init",
          "detail": {
            "id": null,
            "type": "contact"
          },
          "timestamp": 1789641810
        }
      ],
      "callback_time": null,
      "callee_number": "+90XXXXXXXXXX",
      "voicemail_type": null,
      "channel_id": 939,
      "hangup_by": "contact",
      "credited": false,
      "caller_number": "+90XXXXXXXXXX",
      "record_url": null,
      "channel_type": "number",
      "contact_id": 190988
    }
  ],
  "meta": {
    "count": 1,
    "offset": 0,
    "limit": 10
  }
}

A single call record was returned within this range, with count: 1.

C2. Filtering by call direction

The call direction filter takes text rather than a number. Supported values include inbound and outbound.

Test with a text value:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?direction%5Beq%5D=inbound" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

The request sent with inbound returns results successfully.

Test with a numeric value:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?direction%5Beq%5D=1" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "direction": [
      "Value must be a string."
    ]
  }
}

So the value of the direction filter must be sent as text.

C3. Filtering on multiple values

Multiple values can be filtered with the in operator. Values are separated with a comma (,).

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?direction%5Bin%5D=inbound,outbound" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

The result list is filtered as expected.

C4. Filtering missed calls

Missed calls are filtered with the missing_call field. This field accepts the eq operator.

Example usage:

missing_call[eq]=true

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?missing_call%5Beq%5D=true" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

No separate record output was observed in this test.

C5. Using several filters at once

Date and direction filters can be used together in the same request. Filters are joined in the URL with &.

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?started_at%5Bgte%5D=2026-09-17T00%3A00%3A00Z&direction%5Beq%5D=outbound" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

This request works and returns the filtered call records. The test responded with 200 OK.

C6. Date filter on /api/v3/contacts

The /api/v3/contacts endpoint does not support a date filter on the created_at field.

Rather than being silently ignored, the filter makes the API reject the request.

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/contacts?created_at%5Bgte%5D=2026-01-01T00%3A00%3A00Z" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "created_at": [
      "Unexpected field: created_at"
    ]
  }
}

So /api/v3/contacts does not ignore the unsupported created_at filter; it returns an error instead.

C7. A filter operator that does not exist

When an unsupported operator is used, the API rejects the request.

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?started_at%5Byakin%5D=2026-09-01T00%3A00%3A00Z" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "started_at": [
      "#/started_at/yakin: Unexpected field: yakin"
    ]
  }
}

So the unsupported yakin operator is not accepted and the API returns an error.

C8. A malformed date value

The date value in the started_at filter must be in ISO 8601 format.

Test command:

curl -X "GET" \
  "https://use.hipcall.com.tr/api/v3/calls?started_at%5Bgte%5D=dün" \
  -H "accept: application/json" \
  -H "Authorization: Bearer $HIPCALL_API_TOKEN"

Output:

{
  "errors": {
    "started_at": [
      "Invalid datetime format (expected ISO8601)"
    ]
  }
}

The API does not accept the invalid date format and states plainly that it expects an ISO 8601 value.


Findings

Comparing C6, C7 and C8

Placing the API’s reactions to bad filter parameters side by side shows that the system has a strict and safe validation mechanism:

  1. Malformed value (C8): when data outside the expected format (ISO 8601) is sent, the API errors immediately.
  2. Invalid operator (C7): when an unsupported operator ([yakin]) is used, the API rejects the operation.
  3. Unsupported filter field (C6): when you try to filter on a field an endpoint has no equivalent for (created_at), the API does not silently ignore the filter and return everything; it throws an Unexpected field error and stops the flow.

Opinion: this strict behaviour is very good news for a developer. If the API silently ignored a misspelled or unsupported filter, a developer could assume their filter worked and go on to operate on missing or wrong data (e.g. pulling the entire database while believing “I fetched the last month”). Erroring on every wrong step prevents faulty reports and data inconsistencies that would only surface months later.


Endpoint comparison table

Endpointq (search)sort fieldsBracket filters
/api/v3/calls❌ Not supportedstarted_atstarted_at[gte/lte], direction[eq/in], missing_call[eq]
/api/v3/contacts✅ Name, phoneid, first_name, last_name, full_name❌ No date filters (created_at not supported)
/api/v3/companies✅ Supportedid, name❌ Not supported
/api/v3/tasks✅ Supportedid❌ Not supported

Pagination flow

flowchart TD
    A["Start: offset = 0"] --> B["GET /api/v3/calls\n?limit=100&offset=offset"]
    B --> C{HTTP 200 OK?}
    C -- No --> D["Print the error message and stop"]
    C -- Yes --> E["Read meta.count"]
    E --> F["Append the records to the collection"]
    F --> G{"offset + limit < meta.count?"}
    G -- Yes --> H["offset = offset + limit"]
    H --> B
    G -- No --> I["All pages done → write CSV"]

Note: a meta.count-based loop was preferred over “loop until an empty page arrives”. The reason: if the data changes during pagination, the empty-page method can skip the last page or fire an extra request. meta.count is computed once in the loop and stays fixed.


The delivered tool

C# console application: submissions/02-liste-ve-filtreleme/Hipcall.MissedCalls/

It fetches the last 7 days of missed calls and writes them to CSV. Run it with dotnet run. The API key is read from the HIPCALL_API_TOKEN environment variable.

Ask the Community

Have questions or want to share your integration? Join the discussion.

Tools & APIs

Six ways to extend and integrate Hipcall into your systems.

REST API

Access your Hipcall data programmatically. Manage extensions, initiate calls, retrieve call records, and more via a RESTful HTTP API with OAuth 2.0 and API key authentication.

Webhooks

Receive real-time HTTP notifications for 13+ event types — incoming calls, answered calls, hangups, voicemails, and more. Push call data into any system instantly.

Web Service-Based Smart IVRs

Route incoming calls dynamically using your own business logic. Hipcall calls your web service at ring time and routes the call based on your JSON response.

Quick Call

Trigger outbound calls programmatically. Initiate a call from an agent's extension to any number via a single API request — great for click-to-call integrations.

External Management

Manage your Hipcall account from external systems. Provision users, update call flows, and control settings without logging into the dashboard.

Insight Card

Display real-time caller context on the agent's screen the moment a call connects. Push any data — name, company, account balance — from your CRM or ERP via API.