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

Every list endpoint accepts a pagination object with these fields:

FieldTypeDefaultDescription
limitinteger20Maximum items per page (1-100)
cursorstring""Cursor from previous response for next page
offsetinteger0Alternative to cursor — skip N items

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. Pass the next_cursor from the previous response as the cursor in the next request:

Page 1

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
  }
}

Page 2

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",
	},
})

Last Page

When there are no more results, next_cursor is an empty string:

{
  "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:

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?.next_cursor ?? '';
  } while (cursor !== '');

  return allJobs;
}
def get_all_jobs(client):
    all_jobs = []
    cursor = ""

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

        cursor = response.pagination.next_cursor
        if not cursor:
            break

    return all_jobs
func getAllJobs(ctx context.Context, client jobv1connect.JobServiceClient) ([]*jobv1.Job, error) {
	var allJobs []*jobv1.Job
	cursor := ""

	for {
		resp, err := client.List(ctx, connect.NewRequest(&jobv1.ListJobsRequest{
			Pagination: &commonv1.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.