Skip to content
Search Documentation
Search across all documentation pages
Pagination

Pagination

All list endpoints in the Transcodely API support pagination to efficiently traverse large result sets. The API uses cursor-based pagination, which provides stable results even as new resources are created or deleted between requests.

Request Parameters

Most list endpoints accept a pagination object with these fields:

FieldTypeDescription
limitintegerMaximum items per page (1-100). Defaults to 20.
cursorstringCursor from a previous response for the next page. Defaults to "" (start from the first page).
offsetintegerAlternative to cursor — skip N items. Defaults to 0.

Note: Videos.list is the exception. It uses top-level page_size and page_token request fields (and returns next_page_token) instead of the standard pagination object described here. The concept is the same — pass the previous response’s next_page_token back as page_token — but the field names differ.

Basic Example

curl -X POST https://api.transcodely.com/transcodely.v1.JobService/List 
  -H "Authorization: Bearer {{API_KEY}}" 
  -H "X-Organization-ID: org_a1b2c3d4e5" 
  -H "Content-Type: application/json" 
  -d '{
    "pagination": {
      "limit": 10
    }
  }'
const { items, nextCursor } = await client.jobs.list({ pagination: { limit: 10 } });
console.log(items, nextCursor);
page = client.jobs.list(limit=10)
print(page.items, page.next_cursor)
iter := client.Jobs.List(ctx, &transcodely.JobListParams{
	Pagination: &transcodely.PaginationRequest{Limit: 10},
})

Response Metadata

Every list response includes a pagination object:

FieldTypeDescription
next_cursorstringCursor for fetching the next page. Empty if no more pages.
total_countinteger (optional)Total number of matching items, if available
{
  "jobs": [ ... ],
  "pagination": {
    "next_cursor": "eyJpZCI6ImpvYl94OXk4ejd3NnY1In0",
    "total_count": 142
  }
}

Cursor-Based Pagination

Cursor pagination is the recommended approach. Make the first request with just a limit — no cursor. The response carries a next_cursor; pass that value back as the cursor on the following request to fetch the next page, and repeat. When the API returns an empty next_cursor, you have reached the last page and can stop.

First request — no cursor yet. The response’s next_cursor points at the next page:

curl -X POST https://api.transcodely.com/transcodely.v1.JobService/List 
  -H "Authorization: Bearer {{API_KEY}}" 
  -H "X-Organization-ID: org_a1b2c3d4e5" 
  -H "Content-Type: application/json" 
  -d '{
    "pagination": { "limit": 10 }
  }'
const page = await client.jobs.list({ pagination: { limit: 10 } });
console.log(page.items, page.nextCursor);
page = client.jobs.list(limit=10)
print(page.items, page.next_cursor)
iter := client.Jobs.List(ctx, &transcodely.JobListParams{
	Pagination: &transcodely.PaginationRequest{Limit: 10},
})

Response:

{
  "jobs": [ "... 10 jobs ..." ],
  "pagination": {
    "next_cursor": "eyJpZCI6ImpvYl94OXk4ejd3NnY1In0",
    "total_count": 42
  }
}

Next request — pass the previous response’s next_cursor back as cursor to get the following page. (The Python SDK hides the cursor entirely; use auto_paging_iter() to walk pages automatically.)

curl -X POST https://api.transcodely.com/transcodely.v1.JobService/List 
  -H "Authorization: Bearer {{API_KEY}}" 
  -H "X-Organization-ID: org_a1b2c3d4e5" 
  -H "Content-Type: application/json" 
  -d '{
    "pagination": {
      "limit": 10,
      "cursor": "eyJpZCI6ImpvYl94OXk4ejd3NnY1In0"
    }
  }'
const page = await client.jobs.list({
  pagination: { limit: 10, cursor: "eyJpZCI6ImpvYl94OXk4ejd3NnY1In0" },
});
console.log(page.items, page.nextCursor);
# The Python SDK hides the cursor — iterate every page with auto_paging_iter().
for job in client.jobs.list(limit=10).auto_paging_iter():
    print(job.id, job.status)
iter := client.Jobs.List(ctx, &transcodely.JobListParams{
	Pagination: &transcodely.PaginationRequest{
		Limit:  10,
		Cursor: "eyJpZCI6ImpvYl94OXk4ejd3NnY1In0",
	},
})

Keep going until the response comes back with an empty next_cursor — that empty string is the last page, and your signal to stop:

{
  "jobs": [ "... 2 remaining jobs ..." ],
  "pagination": {
    "next_cursor": "",
    "total_count": 42
  }
}

Iterating All Pages

Auto-paginating with the SDKs

The official SDKs hide the cursor loop behind an iterator. Pass any list parameters once and walk every page:

cursor=""
while :; do
  resp=$(curl -s -X POST https://api.transcodely.com/transcodely.v1.JobService/List 
    -H "Authorization: Bearer {{API_KEY}}" 
    -H "X-Organization-ID: org_a1b2c3d4e5" 
    -H "Content-Type: application/json" 
    -d "{"pagination": {"limit": 100, "cursor": "$cursor"}}")
  echo "$resp" | jq -c '.jobs[]'
  cursor=$(echo "$resp" | jq -r '.pagination.next_cursor')
  [ -z "$cursor" ] && break
done
for await (const job of client.jobs.list({ pagination: { limit: 100 } }).autoPage()) {
  console.log(job.id, job.status);
}
for job in client.jobs.list(limit=100).auto_paging_iter():
    print(job.id, job.status)
iter := client.Jobs.List(ctx, &transcodely.JobListParams{
    Pagination: &transcodely.PaginationRequest{Limit: 100},
})
defer iter.Close()
for iter.Next() {
    job := iter.Current()
    log.Printf("%s %s", job.GetId(), job.GetStatus())
}
if err := iter.Err(); err != nil {
    log.Fatal(err)
}

Each iterator stops cleanly when the API returns an empty next_cursor. Errors surface through iter.Err() (Go) or as raised exceptions (TypeScript / Python).

Manual cursor management

If you call the API without an SDK (e.g. directly from a Connect-RPC generated stub), drive the cursor loop by hand — feed each response’s next_cursor back as the next request’s cursor until it comes back empty. Each example below accumulates the jobs from every page into a single list and returns it once the cursor runs out; the loop shape is identical across all four languages.

cursor=""
all_jobs="[]"
while :; do
  resp=$(curl -s -X POST https://api.transcodely.com/transcodely.v1.JobService/List 
    -H "Authorization: Bearer {{API_KEY}}" 
    -H "X-Organization-ID: org_a1b2c3d4e5" 
    -H "Content-Type: application/json" 
    -d "{"pagination": {"limit": 100, "cursor": "$cursor"}}")
  all_jobs=$(jq -s '.[0] + .[1].jobs' <(echo "$all_jobs") <(echo "$resp"))
  cursor=$(echo "$resp" | jq -r '.pagination.next_cursor')
  [ -z "$cursor" ] && break
done
echo "$all_jobs"
async function getAllJobs(client: JobServiceClient): Promise<Job[]> {
  const allJobs: Job[] = [];
  let cursor = '';

  do {
    const response = await client.list({
      pagination: { limit: 100, cursor },
    });

    allJobs.push(...response.jobs);
    cursor = response.pagination?.nextCursor ?? '';
  } while (cursor !== '');

  return allJobs;
}
# Python has no separate raw-stub path — drive the cursor through the SDK's
# Page, which exposes this page's `items` and the `next_cursor` to feed back.
# (`auto_paging_iter()` does exactly this loop for you.)
def get_all_jobs(client):
    all_jobs = []
    cursor = None

    while True:
        page = client.jobs.list(pagination={"limit": 100, "cursor": cursor})
        all_jobs.extend(page.items)

        cursor = page.next_cursor
        if not cursor:
            break

    return all_jobs
// transcodelyv1 / transcodelyv1connect are the packages `buf generate` emits from
// the Transcodely protos. The api repo's own stubs are generated under an
// `internal/` go_package_prefix and are therefore not importable — generate your
// own from the published protos, or use the Go SDK, which vendors them.
func getAllJobs(ctx context.Context, client transcodelyv1connect.JobServiceClient) ([]*transcodelyv1.Job, error) {
	var allJobs []*transcodelyv1.Job
	cursor := ""

	for {
		resp, err := client.List(ctx, connect.NewRequest(&transcodelyv1.ListJobsRequest{
			Pagination: &transcodelyv1.PaginationRequest{
				Limit:  100,
				Cursor: cursor,
			},
		}))
		if err != nil {
			return nil, err
		}

		allJobs = append(allJobs, resp.Msg.Jobs...)
		cursor = resp.Msg.Pagination.NextCursor
		if cursor == "" {
			break
		}
	}

	return allJobs, nil
}

Offset Pagination

As an alternative to cursors, you can use offset-based pagination. This is simpler but less stable — if items are created or deleted between pages, you may see duplicates or skip items.

curl -X POST https://api.transcodely.com/transcodely.v1.JobService/List 
  -H "Authorization: Bearer {{API_KEY}}" 
  -H "X-Organization-ID: org_a1b2c3d4e5" 
  -H "Content-Type: application/json" 
  -d '{
    "pagination": { "limit": 10, "offset": 20 }
  }'
const page = await client.jobs.list({ pagination: { limit: 10, offset: 20 } });
console.log(page.items, page.nextCursor);
page = client.jobs.list(limit=10, offset=20)
print(page.items, page.next_cursor)
iter := client.Jobs.List(ctx, &transcodely.JobListParams{
	Pagination: &transcodely.PaginationRequest{Limit: 10, Offset: 20},
})

Use offset pagination only when you need random access to a specific page (e.g., “jump to page 3”). For sequential traversal, always prefer cursors.

Cursor vs Offset

FeatureCursorOffset
StabilityStable across inserts/deletesMay skip or duplicate items
PerformanceConsistent (index-based)Slower on deep pages
Random accessNot supportedSupported
Recommended forSequential iteration, real-time dataJump-to-page UIs

Best Practices

  1. Use cursor pagination for iterating through results sequentially.
  2. Set limit to the maximum your UI can display — fewer requests means better performance.
  3. Stop when next_cursor is empty — this is the only reliable signal that you have reached the last page.
  4. Do not construct cursors manually — they are opaque tokens. Always use the value returned by the API.
  5. Cache total_count if needed — it may not be available on all endpoints and can be expensive to compute.