The three storage types share a consistent shape: a collection client (list, getOrCreate) and a
single-resource client (get, update, delete, plus storage-specific operations). Run-nested
default storages are reachable via client.run(id).dataset() / .keyValueStore() /
.requestQueue().
The get()/getOrCreate(...) calls return the storage metadata model. All three share the common
getters getId(), getName(), getUserId(), getCreatedAt() (Instant), and getModifiedAt()
(Instant), plus a type-specific size counter:
| Model | Common getters | Type-specific |
|---|---|---|
Dataset |
getId, getName, getUserId, getCreatedAt, getModifiedAt |
getItemCount() (long) |
KeyValueStore |
getId, getName, getUserId, getCreatedAt, getModifiedAt |
— |
RequestQueue |
getId, getName, getUserId, getCreatedAt, getModifiedAt |
getTotalRequestCount() (long) |
Dataset, KeyValueStore, RequestQueue and RequestQueueRequest all extend ApifyResource, so
any field not covered by a typed getter above is still available via the inherited getExtra()
(see the docs index).
| Method | Description |
|---|---|
list(StorageListOptions) |
List datasets. Completes with PaginationList<Dataset>. |
iterate(StorageListOptions, Long chunkSize) |
Lazy Flow.Publisher<Dataset> over all datasets; the options' limit caps the total yielded (null/unset or non-positive = all), chunkSize sets the per-request page size (null = server default). |
getOrCreate(String name) |
Get or create a named dataset (empty name → unnamed). Completes with Dataset. |
getOrCreate(String name, Object schema) |
As above, sending a creation-time dataset schema when a new dataset is created. Completes with Dataset. |
StorageListOptions adds unnamed(Boolean) and ownership(String) on top of offset/limit/desc.
| Method | Description |
|---|---|
get() / update(Object) / delete() |
Metadata CRUD. On a client reached through a run/task with no dataset id of its own (e.g. run.dataset()), a 404 is ambiguous (parent vs. sub-resource gone) and throws NotFoundError instead of resolving to empty/no-op — see Fetching single resources. |
listItems(DatasetListItemsOptions) |
List items. Completes with PaginationList<JsonNode>. |
listItems(DatasetListItemsOptions, Class<T>) |
List items decoded into T. Completes with PaginationList<T>. |
iterateItems(DatasetListItemsOptions) / iterateItems(DatasetListItemsOptions, Long chunkSize) |
A lazy Flow.Publisher<JsonNode> over all items; the options' limit caps the total yielded (null/unset or non-positive = all), the optional chunkSize sets the per-request page size (omitted/null = server default). |
iterateItems(DatasetListItemsOptions, Long chunkSize, Class<T>) |
As above, decoded into T. Returns Flow.Publisher<T>. For typed iteration at the server-default page size, pass a null chunk size: iterateItems(opts, null, T.class). |
downloadItems(DownloadItemsFormat, DatasetDownloadOptions) |
Serialized bytes. DownloadItemsFormat is one of JSON, JSONL, CSV, XLSX, XML, RSS, HTML. Completes with byte[]. |
pushItems(Object) |
Push a single item or a list of items. Completes with no value (CompletableFuture<Void>). |
getStatistics() |
Dataset statistics. Completes with JsonNode; throws NotFoundError if the dataset is gone (no separate "statistics absent" state). |
createItemsPublicUrl(DatasetListItemsOptions, Long expiresInSecs) |
A public (optionally signed) items URL, served as json. Completes with String. |
createItemsPublicUrl(DatasetListItemsOptions, Long expiresInSecs, DownloadItemsFormat format) |
As above, with the URL's serialization format set explicitly. |
Server-side item filters,
unwind, and iteration. The dataset-items endpoint appliesoffset/limitto the raw rows and then transforms them: a filter (skipEmpty,skipHidden,clean,simplified) can drop rows (fewer items returned than scanned), andunwindcan split a row's array field into several items (more items returned than scanned). Where the API reports the number of rows it scanned (X-Apify-Pagination-Count),iterateItemsadvances by that number rather than by the number of items returned, so neither case repeats already-seen items, skips rows, nor ends iteration early. (A reported0is trusted only when the page also returned nothing, since scanning zero rows can never produce items; any other combination falls back to the returned count, as if the header were absent.)
Dataset ds = client.datasets().getOrCreate("my-dataset").join();
client.dataset(ds.getId()).pushItems(List.of(Map.of("url", "https://a.com"))).join();
PaginationList<JsonNode> page = client.dataset(ds.getId()).listItems(new DatasetListItemsOptions().limit(100L)).join();
byte[] csv = client.dataset(ds.getId()).downloadItems(DownloadItemsFormat.CSV, new DatasetDownloadOptions().bom(true)).join();DatasetListItemsOptions fields: offset (Long, number of items to skip), limit (Long,
maximum number of items to return), desc (Boolean, return items newest-first), fields
(List<String>, restrict the output to these source fields), outputFields (List<String>,
positionally renames the fields chosen by fields in the output — the i-th name renames the i-th
fields entry, so it only makes sense together with fields), omit (List<String>, exclude
these fields from the output), skipEmpty (Boolean, skip empty items), skipHidden (Boolean,
skip hidden fields, i.e. those starting with #), clean (Boolean, return only clean —
non-empty, non-hidden — items), unwind (List<String>, expand these fields so each array element
becomes a separate item), flatten (List<String>, flatten these nested fields into dot-notation
keys), view (String, select a predefined dataset view for field selection), simplified
(Boolean, return simplified — flattened and cleaned — items), skipFailedPages (Boolean, skip
items that come from failed pages), signature (String, a pre-shared URL signature granting
access without an API token). downloadItems(...) returns byte[] (the serialized export).
DatasetDownloadOptions wraps a DatasetListItemsOptions (items(DatasetListItemsOptions)) and
adds attachment (Boolean), bom (Boolean), delimiter (String), skipHeaderRow
(Boolean), xmlRoot (String), xmlRow (String), feedTitle (String), feedDescription
(String).
createItemsPublicUrl(DatasetListItemsOptions, Long expiresInSecs[, DownloadItemsFormat format])
completes with a String URL. If the dataset is private, the client fetches it, reads its
URL-signing secret, and appends an HMAC-SHA256 signature (bounded by expiresInSecs, or
non-expiring when null); for public datasets the URL is unsigned. The optional format sets the
serialization the URL serves items in (default json when omitted/null).
Public-URL limitation (matches the JavaScript reference client). The public-URL builders (
createItemsPublicUrl, and the key-value-storegetRecordPublicUrl/createKeysPublicUrl) build the URL from the client's own resource path plus the signature. When called on a last-run nested client (e.g.client.actor(id).lastRun("SUCCEEDED").dataset().createItemsPublicUrl(...)) the returned URL keeps the unpinned.../runs/last/datasetpath and does not carry thestatusfilter, so it may resolve a different run when opened. Build public URLs from a concreteclient.dataset(id)/client.keyValueStore(id)client when you need a stable shareable URL.
list(StorageListOptions), iterate(StorageListOptions, Long chunkSize), getOrCreate(String), and
getOrCreate(String, Object schema) (the latter sends a creation-time store schema), as for
datasets.
| Method | Description |
|---|---|
get() / update(Object) / delete() |
Metadata CRUD. On a client reached through a run/task with no store id of its own (e.g. run.keyValueStore()), a 404 is ambiguous (parent vs. sub-resource gone) and throws NotFoundError instead of resolving to empty/no-op — see Fetching single resources. |
listKeys(ListKeysOptions) |
List keys. Completes with KeyValueStoreKeysPage. |
iterateKeys(ListKeysOptions) / iterateKeys(ListKeysOptions, Long chunkSize) |
A lazy Flow.Publisher<KeyValueStoreKey> over all keys, paging with the cursor (exclusiveStartKey). Note: here the options' limit caps the total number of keys yielded (null/unset or non-positive = all), whereas for listKeys/createKeysPublicUrl the same ListKeysOptions.limit is a single-request page size. chunkSize sets the per-request page size (null = server default). |
recordExists(String key) |
Whether a record exists. Completes with boolean. |
getRecord(String key) / getRecord(String key, GetRecordOptions) |
Fetch a record. Completes with Optional<KeyValueStoreRecord>. |
setRecord(String key, byte[] value, String contentType) |
Store raw bytes. Completes with no value (CompletableFuture<Void>). |
setRecord(String key, byte[] value, String contentType, SetRecordOptions) |
Store raw bytes with write options (SetRecordOptions: timeoutSecs (Long), doNotRetryTimeouts (Boolean)). Completes with no value. |
setRecordJson(String key, Object value) |
Store JSON. Completes with no value. |
deleteRecord(String key) |
Delete a record. Completes with no value. |
getRecordPublicUrl(String key) |
A public (optionally signed) record URL. Completes with String. |
createKeysPublicUrl(Long expiresInSecs) |
A public (optionally signed) key-list URL. Completes with String. |
createKeysPublicUrl(ListKeysOptions, Long expiresInSecs) |
As above, forwarding key-listing filters (limit, prefix, collection, exclusiveStartKey). |
ListKeysOptions fields (all optional): limit(Long), exclusiveStartKey(String),
prefix(String) (restrict to keys with this prefix), collection(String) (a named collection of
keys), signature(String) (a pre-shared URL signature granting access without an API token, used
by createKeysPublicUrl).
KeyValueStore store = client.keyValueStores().getOrCreate("my-store").join();
client.keyValueStore(store.getId()).setRecordJson("OUTPUT", Map.of("answer", 42)).join();
Optional<KeyValueStoreRecord> rec = client.keyValueStore(store.getId()).getRecord("OUTPUT").join();
rec.ifPresent(r -> System.out.println(new String(r.getValue())));GetRecordOptions (for getRecord(String key, GetRecordOptions)) fields: attachment(Boolean)
(request a download disposition) and signature(String) (a pre-computed access signature).
KeyValueStoreRecord exposes getKey(), getValue() (raw bytes) and getContentType().
KeyValueStoreKeysPage exposes getItems() (a list of KeyValueStoreKey with getKey()/getSize()),
isTruncated(), getExclusiveStartKey() and getNextExclusiveStartKey().
Both getRecordPublicUrl and createKeysPublicUrl complete with a String URL, signed for private stores
and unsigned for public ones. They differ because they sign different things: a single record URL
signs the record key directly (there is no expiry to bound), while a key-list URL uses an
expiry-aware storage-content signature — hence only createKeysPublicUrl takes an expiresInSecs
(null = non-expiring).
list(StorageListOptions), iterate(StorageListOptions, Long chunkSize), and getOrCreate(String),
as for datasets. Unlike datasets/key-value stores, there is no getOrCreate(String, Object schema)
overload here — the request-queue creation endpoint does not accept a creation-time schema.
| Method | Description |
|---|---|
get() / update(Object) / delete() |
Metadata CRUD. On a client reached through a run/task with no queue id of its own (e.g. run.requestQueue()), a 404 is ambiguous (parent vs. sub-resource gone) and throws NotFoundError instead of resolving to empty/no-op — see Fetching single resources. |
withClientKey(String) |
A copy that identifies its requests with a stable client key (required for lock operations). |
listHead(Long limit) |
Requests at the head. Completes with RequestQueueHead. |
addRequest(RequestQueueRequest, boolean forefront) |
Add a request. Completes with RequestQueueOperationInfo. |
getRequest(String id) |
Fetch a request. Completes with Optional<RequestQueueRequest>. |
updateRequest(RequestQueueRequest, boolean forefront) |
Update a request. Completes with RequestQueueOperationInfo. |
deleteRequest(String id) |
Delete a request. Completes with no value (CompletableFuture<Void>). |
batchAddRequests(List<RequestQueueRequest>, boolean forefront) |
Add many (auto-chunked at 25 requests and by the API's ~9 MiB payload-size limit; unprocessed requests retried). Completes with BatchAddResult. |
batchAddRequests(List<RequestQueueRequest>, boolean forefront, BatchAddRequestsOptions) |
As above, tuning maxUnprocessedRequestsRetries, maxParallel (bounds how many chunk requests are in flight concurrently) and minDelayBetweenUnprocessedRequestsRetriesMillis. |
batchDeleteRequests(Object) |
Delete many. Completes with BatchDeleteResult. |
listAndLockHead(long lockSecs, Long limit) |
Atomically lock the head. Completes with LockedRequestQueueHead. |
listRequests(ListRequestsOptions) |
List requests. Completes with RequestsList. |
prolongRequestLock(String id, long lockSecs, boolean forefront) |
Extend a lock. Completes with RequestLockInfo. |
deleteRequestLock(String id, boolean forefront) |
Release a lock. Completes with no value. |
unlockRequests() |
Release all the client's locks. Completes with UnlockRequestsResult. |
paginateRequests(Long pageLimit) |
A lazy Flow.Publisher<RequestQueueRequest> over all requests, paging with the queue's forward cursor. Equivalent to paginateRequests(null, pageLimit, null). |
paginateRequests(Long totalLimit, Long chunkSize, List<String> filter) |
As above, with a cap on the total number yielded (null/non-positive = unbounded) and an optional state filter (ListRequestsOptions.FILTER_LOCKED/FILTER_PENDING), matching listRequests's filter. |
The
forefrontparameter.addRequest,updateRequest,batchAddRequests,prolongRequestLockanddeleteRequestLockall take aboolean forefront. It controls queue priority:false(the common case) processes the request in normal FIFO order;trueinserts or moves the request to the front of the queue, so it is returned before anything already queued — use it for urgent/priority work that should jump the line, e.g. re-queuing a failed request for immediate retry or seeding a crawl's very first URLs.
// Retry a failed request ahead of everything else already queued.
RequestQueueClient queue = client.requestQueue("QUEUE_ID");
RequestQueueRequest failed = queue.getRequest("REQUEST_ID").join().orElseThrow();
queue.updateRequest(failed.setNoRetry(false), true).join();Naming exception. Request-queue requests are iterated with
paginateRequests(...)— not aniterate(...)method — because the request-queue listing is cursor-based rather than offset/limit; it always starts from the beginning of the queue (resuming from an explicitexclusiveStartId/cursoris not supported by the publisher — uselistRequests(ListRequestsOptions)directly for that single-page use case). Every other resource uses theiterate/iterateItems/iterateKeysfamily.
RequestQueue rq = client.requestQueues().getOrCreate("my-queue").join();
RequestQueueClient queue = client.requestQueue(rq.getId());
queue.addRequest(new RequestQueueRequest("https://example.com", "example"), false).join();
List<RequestQueueRequest> requests = Publishers.collect(queue.paginateRequests(100L)).join();
requests.forEach(r -> System.out.println(r.getUrl()));Lock lifecycle for distributed crawling.
withClientKey(String)returns a copy of the client that stamps every request with a stable identifier, so the queue can tell which client holds which lock; use the sameRequestQueueClientinstance (or another built with the same client key) for the whole lock/unlock cycle below.listAndLockHeadatomically reserves requests so other workers using a different client key cannot receive them until the lock expires or is explicitly released.
RequestQueueClient queue = client.requestQueue("QUEUE_ID").withClientKey("worker-1");
// Atomically reserve up to 10 requests from the head, locked for 60s.
LockedRequestQueueHead locked = queue.listAndLockHead(60L, 10L).join();
for (RequestQueueRequest request : locked.getItems()) {
boolean handledSuccessfully = true; // replace with your own processing logic/result
if (handledSuccessfully) {
queue.deleteRequest(request.getId()).join();
} else {
// Give up on this one: release its lock so another worker can pick it up instead.
queue.deleteRequestLock(request.getId(), false).join();
}
}
// Still working on a request past its lock's expiry? Extend it instead of losing it mid-process.
queue.prolongRequestLock(locked.getItems().get(0).getId(), 60L, false).join();
// On shutdown, release every lock this client still holds so other workers are not blocked
// waiting for locks this worker will never finish processing.
queue.unlockRequests().join();ListRequestsOptions fields: limit, exclusiveStartId, cursor (mutually exclusive with
exclusiveStartId), and filter(List<String>) restricted to ListRequestsOptions.FILTER_LOCKED /
FILTER_PENDING.
RequestQueueRequest models a request. Its (url, uniqueKey) constructor covers the common case
(uniqueKey is the deduplication key); fluent setters and matching getters cover the rest (unset
fields are omitted on the wire): setId/getId (String), setUrl/getUrl (String),
setUniqueKey/getUniqueKey (String), setMethod/getMethod (String),
setUserData(JsonNode)/getUserData() (JsonNode),
setPayload(String)/getPayload() (String, the HTTP request body),
setHeaders(Map<String, String>)/getHeaders() (Map<String, String>),
setNoRetry(Boolean)/getNoRetry() (Boolean),
setHandledAt(Instant)/getHandledAt() (Instant),
setRetryCount(Integer)/getRetryCount() (Integer),
setLoadedUrl(String)/getLoadedUrl() (String, the URL actually loaded, after redirects), and
setErrorMessages(List<String>)/getErrorMessages() (List<String>, never null — empty when
unset). getLockExpiresAt() (read-only, no setter, Instant) is populated only on items returned
from listAndLockHead.
Return types:
RequestQueueOperationInfo(fromaddRequest/updateRequest):getRequestId(),isWasAlreadyPresent(),isWasAlreadyHandled().RequestQueueHead(fromlistHead):getItems()(a list ofRequestQueueRequest),getLimit(),getQueueModifiedAt()(Instant),isHadMultipleClients().BatchAddResult(frombatchAddRequests):getProcessedRequests()(a list ofRequestQueueOperationInfo) andgetUnprocessedRequests()(a list ofRequestQueueRequest).BatchDeleteResult(frombatchDeleteRequests):getProcessedRequests()(a list ofDeletedRequestInfo, exposinggetId()/getUniqueKey()) andgetUnprocessedRequests()(a list ofRequestQueueRequest).LockedRequestQueueHead(fromlistAndLockHead): asRequestQueueHeadplusgetLockSecs(),isQueueHasLockedRequests()andgetClientKey(); each item'sgetLockExpiresAt()is populated.RequestsList(fromlistRequests):getItems(),getLimit(),getCursor(),getNextCursor()(pass to a furtherlistRequestscall to continue paging).RequestLockInfo(fromprolongRequestLock):getLockExpiresAt()(Instant).UnlockRequestsResult(fromunlockRequests):getUnlockedCount().
batchDeleteRequests(Object) accepts a JSON-serializable list of request identifiers (each with an
id or uniqueKey).