diff --git a/docs/01_introduction/index.md b/docs/01_introduction/index.md
new file mode 100644
index 000000000..c15d1a8bd
--- /dev/null
+++ b/docs/01_introduction/index.md
@@ -0,0 +1,16 @@
+---
+sidebar_label: 'Overview'
+title: 'Overview'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Apify Client is the official library to access the [Apify REST API](https://docs.apify.com/api/v2) from your JavaScript/TypeScript applications.
+
+It runs both in Node.js and browser and provides useful features like automatic retries and convenience functions that improve the experience of using the Apify API. All requests and responses (including errors) are encoded in JSON format with UTF-8 encoding.
+
+## Next steps
+
+- Learn how to install the Apify Client in the [Installation](installation.md) guide
+- Start with the Apify Client using the [Quick start](quick-start.md) guide
diff --git a/docs/01_introduction/installation.md b/docs/01_introduction/installation.md
new file mode 100644
index 000000000..dbe9f9ae1
--- /dev/null
+++ b/docs/01_introduction/installation.md
@@ -0,0 +1,119 @@
+---
+sidebar_label: 'Installation'
+title: 'Installation'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Learn how to install Apify Client using NPM, Yarn, PNPM, or Bun.
+
+---
+
+## Prerequisites
+
+Apify Client requires Node.js version 22 or higher. Node.js is available for download on the [official website](https://nodejs.org/).
+
+Check for your current node version by running:
+
+```bash
+node -v
+```
+
+## Installation
+
+You can install the client via [NPM](https://www.npmjs.com/) or use any other package manager of your choice.
+
+
+
+
+```bash
+npm i apify-client
+```
+
+
+
+
+```bash
+yarn add apify-client
+```
+
+
+
+
+```bash
+pnpm add apify-client
+```
+
+
+
+
+```bash
+bun add apify-client
+```
+
+
+
+
+## Authentication and Initialization
+
+To use the client, you need an [API token](https://docs.apify.com/platform/integrations/api#api-token). You can find your token under [Integrations](https://console.apify.com/account/integrations) tab in Apify Console. Copy the token and initialize the client by providing the token (`MY-APIFY-TOKEN`) as a parameter to the `ApifyClient` constructor.
+
+```js
+// import Apify client
+import { ApifyClient } from 'apify-client';
+
+// Client initialization with the API token
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+});
+```
+
+:::warning Secure access
+
+The API token is used to authorize your requests to the Apify API. You can be charged for the usage of the underlying services, so do not share your API token with untrusted parties or expose it on the client side of your applications
+
+:::
+
+## Usage concepts
+
+The `ApifyClient` interface follows a generic pattern that applies to all of its components. By calling individual methods of `ApifyClient`, specific clients that target individual API resources are created. There are two types of those clients:
+
+- [`actorClient`](/reference/class/ActorClient): a client for the management of a single resource
+- [`actorCollectionClient`](/reference/class/ActorCollectionClient): a client for the collection of resources
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Collection clients do not require a parameter.
+const actorCollectionClient = client.actors();
+// Creates an actor with the name: my-actor.
+const myActor = await actorCollectionClient.create({ name: 'my-actor-name' });
+// List all your used Actors (both own and from Apify Store)
+const { items } = await actorCollectionClient.list();
+```
+
+:::note Resource identification
+
+The resource ID can be either the `id` of the said resource, or a combination of your `username/resource-name`.
+
+:::
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Resource clients accept an ID of the resource.
+const actorClient = client.actor('username/actor-name');
+// Fetches the john-doe/my-actor object from the API.
+const myActor = await actorClient.get();
+// Starts the run of john-doe/my-actor and returns the Run object.
+const myActorRun = await actorClient.start();
+```
+
+## Next steps
+
+- Check out the [quick start](quick-start.md) guide
\ No newline at end of file
diff --git a/docs/01_introduction/quick-start.md b/docs/01_introduction/quick-start.md
new file mode 100644
index 000000000..b0eb8e483
--- /dev/null
+++ b/docs/01_introduction/quick-start.md
@@ -0,0 +1,89 @@
+---
+sidebar_label: 'Quick Start'
+title: 'Quick Start'
+description: 'Get started with the Apify client for JavaScript by running your first Actor and retrieving results.'
+---
+
+Learn how to start Actors and retrieve their results using the Apify Client.
+
+---
+
+## Step 1: Authentication and initialization
+
+To use the client, you need an [API token](/platform/integrations/api#api-token). You can find your token under the [Integrations](https://console.apify.com/account/integrations) tab in Apify Console. Copy the token and initialize the client by providing it as a parameter to the `ApifyClient` constructor.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+});
+```
+
+:::warning Secure access
+
+The API token is used to authorize your requests to the Apify API. You can be charged for the usage of the underlying services, so do not share your API token with untrusted parties or expose it on the client side of your applications.
+
+:::
+
+## Step 2: Running your first Actor
+
+To start an Actor, you need its ID (e.g., `john-doe/my-cool-actor`) and an API token. The Actor's ID is a combination of the Actor name and the Actor owner's username. Use the [`ActorClient`](/reference/class/ActorClient) to run the Actor and wait for it to complete. You can run both your own Actors and [Actors from Apify Store](https://docs.apify.com/platform/actors/running/actors-in-store).
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+});
+
+// Starts an Actor and waits for it to finish
+const { defaultDatasetId } = await client.actor('john-doe/my-cool-actor').call();
+```
+
+### Passing input to the Actor
+
+Actors often require input, such as URLs to scrape, search terms, or other configuration data. You can pass input as a JSON object when starting the Actor using the [`ActorClient.call`](/reference/class/ActorClient#call) method. Actors respect the input schema defined in the Actor's [input schema](https://docs.apify.com/platform/actors/development/actor-definition/input-schema).
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+});
+
+// Runs an Actor with input and waits for it to finish
+const { defaultDatasetId } = await client.actor('john-doe/my-cool-actor').call({
+ startUrls: [{ url: 'https://example.com' }],
+ maxDepth: 3,
+});
+```
+
+## Step 3: Getting results from the dataset
+
+To get the results from the dataset, you can use the [`DatasetClient`](/reference/class/DatasetClient) ([`ApifyClient.dataset`](/reference/class/ApifyClient#dataset)) and [`DatasetClient.listItems`](/reference/class/DatasetClient#listItems) method. You need to pass the dataset ID to define which dataset you want to access. You can get the dataset ID from the Actor's run object (represented by `defaultDatasetId`).
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+});
+
+// Starts an Actor and waits for it to finish
+const { defaultDatasetId } = await client.actor('john-doe/my-cool-actor').call();
+
+// Lists items from the Actor's dataset
+const { items } = await client.dataset(defaultDatasetId).listItems();
+
+// Process the results
+items.forEach((item) => {
+ console.log(item);
+});
+```
+
+:::note Dataset access
+
+Running an Actor might take time, depending on the Actor's complexity and the amount of data it processes. If you want only to get data and have an immediate response, you should access the existing dataset of the finished [Actor run](https://docs.apify.com/platform/actors/running/runs-and-builds#runs).
+
+:::
\ No newline at end of file
diff --git a/docs/02_concepts/01_authentication.md b/docs/02_concepts/01_authentication.md
new file mode 100644
index 000000000..efd6e8e7f
--- /dev/null
+++ b/docs/02_concepts/01_authentication.md
@@ -0,0 +1,107 @@
+---
+sidebar_label: 'Authentication'
+title: 'Authentication'
+description: 'Learn how to authenticate with the Apify API using API tokens and manage credentials securely in your applications.'
+---
+
+## API Token Authentication
+
+To use the Apify Client, you need an [API token](https://docs.apify.com/platform/integrations/api#api-token). The API token authorizes your requests to the Apify API and identifies your account.
+
+### Getting Your API Token
+
+You can find your API token in the [Integrations](https://console.apify.com/account/integrations) tab in Apify Console:
+
+1. Log in to [Apify Console](https://console.apify.com/)
+2. Navigate to **Settings** → **Integrations**
+3. Copy your API token from the **Personal API tokens** section
+
+### Initializing the Client
+
+Pass your API token to the `ApifyClient` constructor:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+// Initialize the client with your API token
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+});
+```
+
+### Using Environment Variables
+
+For better security, store your API token in an environment variable rather than hardcoding it:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+// Initialize with token from environment variable
+const client = new ApifyClient({
+ token: process.env.APIFY_TOKEN,
+});
+```
+
+Set the environment variable before running your script:
+
+```bash
+export APIFY_TOKEN='MY-APIFY-TOKEN'
+node your-script.js
+```
+
+:::tip Best Practice
+
+The Apify Client automatically reads the `APIFY_TOKEN` environment variable if you don't provide a token explicitly:
+
+```js
+// This will use process.env.APIFY_TOKEN automatically
+const client = new ApifyClient();
+```
+
+:::
+
+## Token Storage Best Practices
+
+Follow these security guidelines when working with API tokens:
+
+- **Never commit tokens to version control** - Use environment variables or secret management tools
+- **Don't expose tokens on the client side** - Only use API tokens in server-side code
+- **Rotate tokens regularly** - Generate new tokens periodically and revoke old ones
+- **Use separate tokens for different projects** - Create multiple tokens for better access control
+- **Store tokens securely** - Use encrypted storage or secure secret management services
+
+:::warning Secure Access
+
+The API token is used to authorize your requests to the Apify API. You can be charged for the usage of the underlying services, so do not share your API token with untrusted parties or expose it on the client side of your applications.
+
+:::
+
+## Handling Authentication Errors
+
+If authentication fails, the client will throw an error with a `401 Unauthorized` status code:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'invalid-token' });
+
+try {
+ await client.user().get();
+} catch (error) {
+ if (error.statusCode === 401) {
+ console.error('Authentication failed: Invalid API token');
+ }
+}
+```
+
+Common authentication issues:
+
+- **Invalid token format** - Ensure you copied the entire token
+- **Expired token** - Tokens can be revoked; generate a new one
+- **Missing token** - Make sure the token is properly set
+- **Wrong token** - Verify you're using the correct token for your account
+
+## Next Steps
+
+- Learn about [client architecture](02_client_architecture.md) to understand how clients work
+- See [error handling](03_error_handling.md) for comprehensive error management strategies
diff --git a/docs/02_concepts/02_client_architecture.md b/docs/02_concepts/02_client_architecture.md
new file mode 100644
index 000000000..1c58f1786
--- /dev/null
+++ b/docs/02_concepts/02_client_architecture.md
@@ -0,0 +1,206 @@
+---
+sidebar_label: 'Client Architecture'
+title: 'Client Architecture'
+description: 'Understand the hierarchical structure of Apify Client, including collection clients, resource clients, and nested client patterns.'
+---
+
+## Overview
+
+The Apify Client follows a hierarchical pattern where the main `ApifyClient` instance creates specialized clients for different API resources. This architecture mirrors the structure of the Apify API itself, making it intuitive to navigate between resources.
+
+## Client Hierarchy
+
+```
+ApifyClient
+├── Collection Clients (manage multiple resources)
+│ ├── actors() → ActorCollectionClient
+│ ├── datasets() → DatasetCollectionClient
+│ ├── keyValueStores() → KeyValueStoreCollectionClient
+│ └── ...
+└── Resource Clients (manage single resource)
+ ├── actor(id) → ActorClient
+ ├── dataset(id) → DatasetClient
+ ├── keyValueStore(id) → KeyValueStoreClient
+ └── ...
+```
+
+## Two Types of Clients
+
+### Collection Clients
+
+Collection clients manage operations on multiple resources. They don't require any parameters and provide methods like `list()`, `create()`, and `getOrCreate()`.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Collection client - no ID required
+const actorCollectionClient = client.actors();
+
+// List all actors
+const { items } = await actorCollectionClient.list();
+
+// Create a new actor
+const newActor = await actorCollectionClient.create({
+ name: 'my-actor-name'
+});
+```
+
+**Common collection client methods:**
+- `list()` - List resources with pagination
+- `create()` - Create a new resource
+- `getOrCreate()` - Get existing or create new resource
+
+### Resource Clients
+
+Resource clients manage operations on a single, specific resource. They require a resource ID and provide methods like `get()`, `update()`, and `delete()`.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Resource client - requires ID
+const actorClient = client.actor('username/actor-name');
+
+// Get actor details
+const actor = await actorClient.get();
+
+// Start the actor
+const run = await actorClient.start();
+
+// Update actor settings
+await actorClient.update({
+ name: 'new-name'
+});
+```
+
+**Common resource client methods:**
+- `get()` - Fetch resource details
+- `update()` - Modify resource
+- `delete()` - Remove resource
+- Resource-specific methods (e.g., `start()` for actors)
+
+## Resource Identification
+
+Resources can be identified in two ways:
+
+1. **By ID**: The unique identifier of the resource
+ ```js
+ client.actor('dSCLg0C3YEZ83HzYX')
+ ```
+
+2. **By username/name**: A combination of owner's username and resource name
+ ```js
+ client.actor('john-doe/my-actor')
+ ```
+
+:::note Resource Identification
+
+The resource ID can be either the `id` of the said resource, or a combination of your `username/resource-name`.
+
+:::
+
+## Nested Clients
+
+Clients can return other clients to simplify working with nested resources. This is particularly useful for accessing related resources like runs of an actor or datasets of a run.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Get an actor client
+const actorClient = client.actor('username/actor-name');
+
+// Get the runs collection client for this actor
+const runsClient = actorClient.runs();
+
+// List recent runs
+const { items } = await runsClient.list({ limit: 10 });
+
+// Access specific run
+const runClient = actorClient.run('run-id');
+
+// Access the run's dataset directly
+const runDataset = runClient.dataset();
+const { items: datasetItems } = await runDataset.listItems();
+```
+
+### Nested Client Pattern
+
+The nested client pattern allows for intuitive traversal of related resources:
+
+```
+ActorClient
+└── runs() → ActorRunCollectionClient
+ └── run(id) → ActorRunClient
+ ├── dataset() → DatasetClient
+ ├── keyValueStore() → KeyValueStoreClient
+ └── log() → LogClient
+```
+
+## When to Use Each Pattern
+
+### Use Collection Clients When:
+- Listing multiple resources
+- Creating new resources
+- Searching or filtering resources
+- You don't know the specific resource ID
+
+### Use Resource Clients When:
+- Working with a specific resource
+- Starting an actor
+- Fetching data from a specific dataset
+- Updating or deleting a resource
+
+### Use Nested Clients When:
+- Accessing related resources (e.g., actor's runs)
+- Following resource relationships
+- Working with run-specific data (datasets, logs)
+- Simplifying access to deeply nested resources
+
+## Client Method Chaining
+
+The client architecture allows for elegant method chaining:
+
+```js
+// Chain through nested resources
+const items = await client
+ .actor('username/actor-name')
+ .lastRun({ status: 'SUCCEEDED' })
+ .dataset()
+ .listItems();
+```
+
+This pattern is more readable than:
+
+```js
+// Without chaining
+const actorClient = client.actor('username/actor-name');
+const runClient = actorClient.lastRun({ status: 'SUCCEEDED' });
+const datasetClient = runClient.dataset();
+const { items } = await datasetClient.listItems();
+```
+
+## Type Safety
+
+All clients are fully typed when using TypeScript, providing autocomplete and type checking:
+
+```typescript
+import { ApifyClient } from 'apify-client';
+import type { ActorClient, ActorCollectionClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// TypeScript knows the return types
+const actorCollection: ActorCollectionClient = client.actors();
+const actorResource: ActorClient = client.actor('my-actor');
+```
+
+## Next Steps
+
+- Learn about [nested clients](06_nested_clients.md) in detail
+- See [authentication](01_authentication.md) for setting up the main client
+- Explore [pagination](05_pagination.md) when working with collection clients
diff --git a/docs/02_concepts/03_error_handling.md b/docs/02_concepts/03_error_handling.md
new file mode 100644
index 000000000..688413690
--- /dev/null
+++ b/docs/02_concepts/03_error_handling.md
@@ -0,0 +1,308 @@
+---
+sidebar_label: 'Error Handling'
+title: 'Error Handling'
+description: 'Learn how to handle errors and exceptions when using the Apify Client, including retry logic and common HTTP status codes.'
+---
+
+## ApifyApiError Class
+
+When an API request fails, the Apify Client throws an `ApifyApiError` exception. This error class wraps the JSON errors returned by the API and enriches them with additional context for easier debugging.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+try {
+ const { items } = await client.dataset('non-existing-dataset-id').listItems();
+} catch (error) {
+ // The error is an instance of ApifyApiError
+ const { message, type, statusCode, clientMethod, path } = error;
+
+ // Log error details for debugging
+ console.log({
+ message, // Human-readable error message
+ statusCode, // HTTP status code (404, 401, 500, etc.)
+ clientMethod, // Client method that failed
+ type, // Error type identifier
+ path // API endpoint path
+ });
+}
+```
+
+## Error Properties
+
+The `ApifyApiError` provides these useful properties:
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `message` | `string` | Human-readable error description |
+| `statusCode` | `number` | HTTP status code (e.g., 404, 401, 500) |
+| `type` | `string` | Error type identifier from the API |
+| `clientMethod` | `string` | The client method that threw the error |
+| `path` | `string` | The API endpoint path that was called |
+| `originalError` | `object` | The original error response from the API |
+
+## Common HTTP Status Codes
+
+### 401 Unauthorized
+
+Authentication failed or API token is invalid:
+
+```js
+try {
+ await client.user().get();
+} catch (error) {
+ if (error.statusCode === 401) {
+ console.error('Authentication failed: Check your API token');
+ // Handle invalid credentials
+ }
+}
+```
+
+### 404 Not Found
+
+The requested resource doesn't exist:
+
+```js
+try {
+ const actor = await client.actor('non-existent-actor').get();
+} catch (error) {
+ if (error.statusCode === 404) {
+ console.error('Actor not found');
+ // Handle missing resource
+ }
+}
+```
+
+### 429 Too Many Requests
+
+Rate limit exceeded:
+
+```js
+try {
+ await client.actor('my-actor').start();
+} catch (error) {
+ if (error.statusCode === 429) {
+ console.error('Rate limit exceeded. Please wait before retrying.');
+ // Implement exponential backoff
+ }
+}
+```
+
+### 500 Internal Server Error
+
+Server-side error occurred:
+
+```js
+try {
+ await client.actor('my-actor').start();
+} catch (error) {
+ if (error.statusCode === 500) {
+ console.error('Server error. Please try again later.');
+ // Implement retry logic
+ }
+}
+```
+
+## Comprehensive Error Handling Pattern
+
+Here's a robust error handling pattern for production code:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
+
+async function runActorSafely(actorId, input) {
+ try {
+ const run = await client.actor(actorId).call(input);
+ return { success: true, data: run };
+ } catch (error) {
+ // Handle specific error cases
+ switch (error.statusCode) {
+ case 401:
+ return {
+ success: false,
+ error: 'Authentication failed. Check your API token.'
+ };
+
+ case 404:
+ return {
+ success: false,
+ error: `Actor '${actorId}' not found.`
+ };
+
+ case 429:
+ return {
+ success: false,
+ error: 'Rate limit exceeded. Please retry later.',
+ retryable: true
+ };
+
+ case 500:
+ case 502:
+ case 503:
+ return {
+ success: false,
+ error: 'Server error occurred. Please retry.',
+ retryable: true
+ };
+
+ default:
+ return {
+ success: false,
+ error: error.message || 'Unknown error occurred',
+ details: {
+ statusCode: error.statusCode,
+ type: error.type,
+ path: error.path
+ }
+ };
+ }
+ }
+}
+
+// Usage
+const result = await runActorSafely('john-doe/my-actor', { url: 'https://example.com' });
+
+if (result.success) {
+ console.log('Actor run successful:', result.data);
+} else {
+ console.error('Actor run failed:', result.error);
+
+ if (result.retryable) {
+ // Implement retry logic
+ console.log('This error is retryable. Consider retrying...');
+ }
+}
+```
+
+## Network Error Handling
+
+Network errors (connection failures, timeouts) are also thrown as errors:
+
+```js
+try {
+ await client.actor('my-actor').start();
+} catch (error) {
+ if (error.code === 'ECONNREFUSED') {
+ console.error('Cannot connect to Apify API');
+ } else if (error.code === 'ETIMEDOUT') {
+ console.error('Request timed out');
+ } else if (!error.statusCode) {
+ console.error('Network error:', error.message);
+ }
+}
+```
+
+## Retry Logic with Exponential Backoff
+
+For transient errors, implement retry logic with exponential backoff:
+
+```js
+async function retryWithBackoff(fn, maxRetries = 3, initialDelay = 1000) {
+ let lastError;
+
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
+ try {
+ return await fn();
+ } catch (error) {
+ lastError = error;
+
+ // Only retry on specific errors
+ if (error.statusCode === 429 || error.statusCode >= 500) {
+ const delay = initialDelay * Math.pow(2, attempt);
+ console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
+ await new Promise(resolve => setTimeout(resolve, delay));
+ } else {
+ // Don't retry on client errors (4xx except 429)
+ throw error;
+ }
+ }
+ }
+
+ throw lastError;
+}
+
+// Usage
+try {
+ const run = await retryWithBackoff(
+ () => client.actor('my-actor').start(),
+ 3, // max retries
+ 1000 // initial delay in ms
+ );
+ console.log('Actor started:', run.id);
+} catch (error) {
+ console.error('Failed after retries:', error.message);
+}
+```
+
+## Validation Errors
+
+The API returns validation errors when input doesn't meet requirements:
+
+```js
+try {
+ await client.actors().create({
+ // Missing required 'name' field
+ });
+} catch (error) {
+ if (error.statusCode === 400) {
+ console.error('Validation error:', error.message);
+ // Handle validation failure
+ }
+}
+```
+
+## Graceful Degradation
+
+Implement graceful degradation for non-critical operations:
+
+```js
+async function getActorWithFallback(actorId) {
+ try {
+ return await client.actor(actorId).get();
+ } catch (error) {
+ console.warn(`Failed to fetch actor: ${error.message}`);
+
+ // Return fallback data
+ return {
+ id: actorId,
+ name: 'Unknown Actor',
+ unavailable: true
+ };
+ }
+}
+```
+
+## Debugging Tips
+
+Enable detailed logging to debug errors:
+
+```js
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+ // Add custom request interceptor for debugging
+});
+
+try {
+ await client.actor('my-actor').start();
+} catch (error) {
+ // Log full error details
+ console.error('Full error:', JSON.stringify({
+ message: error.message,
+ statusCode: error.statusCode,
+ type: error.type,
+ clientMethod: error.clientMethod,
+ path: error.path,
+ stack: error.stack
+ }, null, 2));
+}
+```
+
+## Next Steps
+
+- Learn about [retries](04_retries.md) for automatic retry handling
+- See [authentication](01_authentication.md) for handling auth errors
+- Explore [client architecture](02_client_architecture.md) to understand error contexts
diff --git a/docs/02_concepts/04_retries.md b/docs/02_concepts/04_retries.md
new file mode 100644
index 000000000..99808d5df
--- /dev/null
+++ b/docs/02_concepts/04_retries.md
@@ -0,0 +1,17 @@
+---
+sidebar_label: 'Retries'
+title: 'Retries'
+---
+
+Network communication sometimes fails. The client will automatically retry requests that failed due to a network error, an internal error of the Apify API (HTTP 500+), or a rate limit error (HTTP 429). By default, it will retry up to 8 times. The first retry will be attempted after ~500ms, the second after ~1000ms, and so on. You can configure those parameters using the `maxRetries` and `minDelayBetweenRetriesMillis` options of the `ApifyClient` constructor.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({
+ token: 'MY-APIFY-TOKEN',
+ maxRetries: 8,
+ minDelayBetweenRetriesMillis: 500, // 0.5s
+ timeoutSecs: 360, // 6 mins
+});
+```
diff --git a/docs/02_concepts/05_pagination.md b/docs/02_concepts/05_pagination.md
new file mode 100644
index 000000000..cc0a54c14
--- /dev/null
+++ b/docs/02_concepts/05_pagination.md
@@ -0,0 +1,48 @@
+---
+sidebar_label: 'Pagination'
+title: 'Pagination'
+---
+
+Most methods named `list` or `listSomething` return a [`Promise`](/reference/interface/PaginatedList).
+
+:::note Expection of pagination
+
+There are some exceptions though, like `listKeys` or `listHead` which paginate differently.
+
+:::
+
+The results you're looking for are always stored under `items` and you can use the `limit` property to get only a subset of results. Other props are also available, depending on the method.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Resource clients accept an ID of the resource.
+const datasetClient = client.dataset('dataset-id');
+
+// Number of items per page
+const limit = 1000;
+// Initial offset
+let offset = 0;
+// Array to store all items
+let allItems = [];
+
+while (true) {
+ const { items, total } = await datasetClient.listItems({ limit, offset });
+
+ console.log(`Fetched ${items.length} items`);
+
+ // Merge new items with other already loaded items
+ allItems.push(...items);
+
+ // If there are no more items to fetch, exit the loading
+ if (offset + limit >= total) {
+ break;
+ }
+
+ offset += limit;
+}
+
+console.log(`Overall fetched ${allItems.length} items`);
+```
diff --git a/docs/02_concepts/06_nested_clients.md b/docs/02_concepts/06_nested_clients.md
new file mode 100644
index 000000000..2096d0877
--- /dev/null
+++ b/docs/02_concepts/06_nested_clients.md
@@ -0,0 +1,47 @@
+---
+sidebar_label: 'Nested clients'
+title: 'Nested clients'
+---
+
+Sometimes clients return other clients. That's to simplify working with nested collections, such as runs of a given Actor.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+const actorClient = client.actor('username/actor-name');
+const runsClient = actorClient.runs();
+// Lists the last 10 runs of your Actor.
+const { items } = await runsClient.list({
+ limit: 10,
+ desc: true,
+});
+
+// Select the last run of your Actor that finished
+// with a SUCCEEDED status.
+const lastSucceededRunClient = actorClient.lastRun({ status: 'SUCCEEDED' });
+// Fetches items from the run's dataset.
+const { items } = await lastSucceededRunClient.dataset().listItems();
+```
+
+The quick access to `dataset` and other storage directly from the run client can be used with the [`lastRun()`](/reference/class/ActorClient#lastRun) method.
+
+## Features
+
+Based on the endpoint, the client automatically extracts the relevant data and returns it in the expected format. Date strings are automatically converted to `Date` objects. For exceptions, the client throws an [`ApifyApiError`](/reference/class/ApifyApiError), which wraps the plain JSON errors returned by API and enriches them with other contexts for easier debugging.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+try {
+ const { items } = await client.dataset('non-existing-dataset-id').listItems();
+} catch (error) {
+ // The error is an instance of ApifyApiError
+ const { message, type, statusCode, clientMethod, path } = error;
+ // Log error for easier debugging
+ console.log({ message, statusCode, clientMethod, type });
+}
+```
diff --git a/docs/03_guides/01_passing_input_to_actors.md b/docs/03_guides/01_passing_input_to_actors.md
new file mode 100644
index 000000000..46909b3e2
--- /dev/null
+++ b/docs/03_guides/01_passing_input_to_actors.md
@@ -0,0 +1,60 @@
+---
+sidebar_label: Passing input
+title: 'Passing input'
+---
+
+The fastest way to get results from an Actor is to pass input directly to the `call` function.
+Input can be passed to `call` function and the reference of running Actor (or wait for finish) is available in `runData` variable.
+
+This example starts an Actor that scrapes 20 posts from the Instagram website based on the hashtag.
+
+```javascript
+import { ApifyClient } from 'apify-client';
+
+// Client initialization with the API token
+const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
+
+const actorClient = client.actor('apify/instagram-hashtag-scraper');
+
+const input = { hashtags: ['rainbow'], resultsLimit: 20 };
+
+// Run the Actor and wait for it to finish up to 60 seconds.
+// Input is not persisted for next runs.
+const runData = await actorClient.call(input, { waitSecs: 60 });
+
+console.log('Run data:');
+console.log(runData);
+```
+
+### Run multiple inputs
+
+To run multiple inputs with the same Actor, most convenient way is to create multiple [tasks](https://docs.apify.com/platform/actors/running/tasks) with different inputs. Task input is persisted on Apify platform when task is created.
+
+```javascript
+import { ApifyClient } from 'apify-client';
+
+// Client initialization with the API token
+const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
+
+const animalsHashtags = ['zebra', 'lion', 'hippo'];
+
+// Multiple input schemas for one Actor can be persisted in tasks.
+// Tasks are saved in the Apify platform and can be run multiple times.
+const socialsTasksPromises = animalsHashtags.map((hashtag) =>
+ client.tasks().create({
+ actId: 'apify/instagram-hashtag-scraper',
+ name: `hashtags-${hashtag}`,
+ input: { hashtags: [hashtag], resultsLimit: 20 },
+ options: { memoryMbytes: 1024 },
+ }),
+);
+
+// Create all tasks in parallel
+const createdTasks = await Promise.all(socialsTasksPromises);
+
+console.log('Created tasks:');
+console.log(createdTasks);
+
+// Run all tasks in parallel
+await Promise.all(createdTasks.map((task) => client.task(task.id).call()));
+```
diff --git a/docs/03_guides/02_retrieving_datasets.md b/docs/03_guides/02_retrieving_datasets.md
new file mode 100644
index 000000000..091ac9d07
--- /dev/null
+++ b/docs/03_guides/02_retrieving_datasets.md
@@ -0,0 +1,275 @@
+---
+sidebar_label: 'Retrieving Datasets'
+title: 'Retrieving Datasets'
+description: 'Learn how to retrieve and work with data from Apify datasets, including pagination, filtering, and accessing data from Actor runs.'
+---
+
+## What are Datasets?
+
+[Datasets](https://docs.apify.com/platform/storage/dataset) are storage containers for structured data on the Apify platform. They are typically used to store results from Actor runs, such as scraped web data, processed information, or any structured output.
+
+## Getting Dataset from Actor Run
+
+When an Actor finishes running, it typically stores its results in a dataset. You can access this dataset using the `defaultDatasetId` from the run object:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Start an Actor and wait for it to finish
+const { defaultDatasetId } = await client.actor('john-doe/my-cool-actor').call();
+
+// Retrieve items from the Actor's dataset
+const { items } = await client.dataset(defaultDatasetId).listItems();
+
+console.log(`Retrieved ${items.length} items from the dataset`);
+```
+
+## Accessing Existing Datasets
+
+If you already know the dataset ID, you can access it directly:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Access dataset by ID
+const { items } = await client.dataset('dataset-id').listItems();
+```
+
+:::info Dataset Access
+
+Running an Actor might take time, depending on the Actor's complexity and the amount of data it processes. If you want only to get data and have an immediate response, you should access the existing dataset of a finished [Actor run](https://docs.apify.com/platform/actors/running/runs-and-builds#runs).
+
+:::
+
+## Listing Dataset Items
+
+The `listItems()` method retrieves data from a dataset with various options:
+
+### Basic Usage
+
+```js
+const { items } = await client.dataset('dataset-id').listItems();
+
+// Process the items
+items.forEach(item => {
+ console.log(item);
+});
+```
+
+### With Pagination
+
+```js
+// Get first 100 items
+const { items, total, count, offset, limit } = await client
+ .dataset('dataset-id')
+ .listItems({
+ limit: 100,
+ offset: 0
+ });
+
+console.log(`Retrieved ${count} of ${total} total items`);
+```
+
+### Filtering Fields
+
+Retrieve only specific fields to reduce bandwidth:
+
+```js
+const { items } = await client
+ .dataset('dataset-id')
+ .listItems({
+ fields: ['name', 'price', 'url'],
+ limit: 50
+ });
+```
+
+### Excluding Fields
+
+Exclude specific fields from the response:
+
+```js
+const { items } = await client
+ .dataset('dataset-id')
+ .listItems({
+ omit: ['largeField', 'unusedData'],
+ limit: 50
+ });
+```
+
+## Direct Dataset Access from Run
+
+You can access a run's dataset directly without storing the dataset ID:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Start an Actor
+const run = await client.actor('john-doe/my-actor').start();
+
+// Wait for the run to finish
+await client.waitForFinish(run.id);
+
+// Access dataset directly from run client
+const runClient = client.run(run.id);
+const { items } = await runClient.dataset().listItems();
+```
+
+## Accessing Last Run's Dataset
+
+Use the `lastRun()` method to access the most recent run's dataset:
+
+```js
+const lastRunClient = client.actor('username/actor-name').lastRun({ status: 'SUCCEEDED' });
+
+// Access the dataset from the last successful run
+const { items } = await lastRunClient.dataset().listItems();
+```
+
+## Dataset Item Formats
+
+You can download dataset items in different formats:
+
+### JSON Format (default)
+
+```js
+const { items } = await client.dataset('dataset-id').listItems();
+// Returns JSON array
+```
+
+### CSV Format
+
+```js
+// Download as CSV (for external processing)
+const datasetClient = client.dataset('dataset-id');
+
+// Get download URL for CSV
+const csvUrl = datasetClient.downloadItems('csv');
+console.log(`Download CSV from: ${csvUrl}`);
+```
+
+### Other Formats
+
+The dataset API supports multiple formats:
+- JSON (default)
+- CSV
+- XLSX
+- HTML
+- XML
+- RSS
+
+## Streaming Large Datasets
+
+For large datasets, use pagination to avoid loading all data at once:
+
+```js
+async function processLargeDataset(datasetId) {
+ const BATCH_SIZE = 1000;
+ let offset = 0;
+ let hasMore = true;
+
+ while (hasMore) {
+ const { items, count } = await client
+ .dataset(datasetId)
+ .listItems({
+ limit: BATCH_SIZE,
+ offset: offset
+ });
+
+ // Process batch
+ for (const item of items) {
+ await processItem(item);
+ }
+
+ offset += count;
+ hasMore = count === BATCH_SIZE;
+
+ console.log(`Processed ${offset} items...`);
+ }
+}
+
+await processLargeDataset('my-dataset-id');
+```
+
+## Error Handling
+
+Always handle errors when retrieving datasets:
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+try {
+ const { items } = await client.dataset('dataset-id').listItems();
+
+ if (items.length === 0) {
+ console.log('Dataset is empty');
+ } else {
+ console.log(`Retrieved ${items.length} items`);
+ }
+} catch (error) {
+ if (error.statusCode === 404) {
+ console.error('Dataset not found');
+ } else {
+ console.error('Failed to retrieve dataset:', error.message);
+ }
+}
+```
+
+## Checking Dataset Info
+
+Get metadata about a dataset before retrieving items:
+
+```js
+const dataset = await client.dataset('dataset-id').get();
+
+console.log({
+ id: dataset.id,
+ name: dataset.name,
+ itemCount: dataset.itemCount,
+ createdAt: dataset.createdAt,
+ modifiedAt: dataset.modifiedAt
+});
+
+// Only fetch items if dataset has data
+if (dataset.itemCount > 0) {
+ const { items } = await client.dataset('dataset-id').listItems();
+}
+```
+
+## Combining Multiple Datasets
+
+To combine data from multiple datasets, retrieve and merge them:
+
+```js
+async function combineDatasets(datasetIds) {
+ const allItems = [];
+
+ for (const datasetId of datasetIds) {
+ try {
+ const { items } = await client.dataset(datasetId).listItems();
+ allItems.push(...items);
+ } catch (error) {
+ console.error(`Failed to retrieve dataset ${datasetId}:`, error.message);
+ }
+ }
+
+ return allItems;
+}
+
+const datasetIds = ['dataset-1', 'dataset-2', 'dataset-3'];
+const combinedData = await combineDatasets(datasetIds);
+console.log(`Combined ${combinedData.length} items from ${datasetIds.length} datasets`);
+```
+
+## Next Steps
+
+- Learn about [joining datasets](03_joining_datasets.md) for more complex data merging
+- See [pagination](../02_concepts/05_pagination.md) concepts for efficient data retrieval
+- Explore [passing input to actors](01_passing_input_to_actors.md) to configure what data gets scraped
diff --git a/docs/03_guides/03_joining_datasets.md b/docs/03_guides/03_joining_datasets.md
new file mode 100644
index 000000000..83cfaeac9
--- /dev/null
+++ b/docs/03_guides/03_joining_datasets.md
@@ -0,0 +1,35 @@
+---
+sidebar_label: Joining datasets
+title: 'Joining datasets'
+---
+
+Actor data is stored in [datasets](https://docs.apify.com/platform/storage/dataset), which can be retrieved from Actor runs. You can list dataset items with pagination and merge multiple datasets together for easier analysis. Datasets can be exported to various formats (CSV, JSON, XLSX, XML) or processed using [Integrations](https://docs.apify.com/platform/integrations).
+
+```javascript
+import { ApifyClient } from 'apify-client';
+
+// Client initialization with the API token
+const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
+
+const actorClient = client.actor('apify/instagram-hashtag-scraper');
+
+const actorRuns = actorClient.runs();
+
+// See pagination to understand how to get more datasets
+const actorDatasets = await actorRuns.list({ limit: 20 });
+
+console.log('Actor datasets:');
+console.log(actorDatasets);
+
+const mergingDataset = await client.datasets().getOrCreate('merge-dataset');
+
+for (const datasetItem of actorDatasets.items) {
+ // Dataset items can be handled here. Dataset items can be paginated
+ const datasetItems = await client.dataset(datasetItem.defaultDatasetId).listItems({ limit: 1000 });
+
+ // Items can be pushed to single dataset
+ await client.dataset(mergingDataset.id).pushItems(datasetItems.items);
+
+ // ...
+}
+```
\ No newline at end of file
diff --git a/docs/03_guides/04_using_webhooks.md b/docs/03_guides/04_using_webhooks.md
new file mode 100644
index 000000000..8668c6470
--- /dev/null
+++ b/docs/03_guides/04_using_webhooks.md
@@ -0,0 +1,59 @@
+---
+sidebar_label: Webhooks
+title: 'Webhooks'
+---
+
+[Webhooks](https://docs.apify.com/platform/integrations/webhooks) can be used to get notifications about Actor runs.
+For example, a webhook can be triggered when an Actor run finishes successfully.
+Webhook can receive dataset ID for further processing.
+
+Initialization of webhook:
+
+```javascript
+import { ApifyClient } from 'apify-client';
+
+// Client initialization with the API token
+const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
+
+const webhooksClient = client.webhooks();
+
+await webhooksClient.create({
+ description: 'Instagram hashtag actor succeeded',
+ condition: { actorId: 'reGe1ST3OBgYZSsZJ' }, // Actor ID of apify/instagram-hashtag-scraper
+ // Request URL can be generated using https://webhook.site. Any REST server can be used
+ requestUrl: 'https://webhook.site/CUSTOM_WEBHOOK_ID',
+ eventTypes: ['ACTOR.RUN.SUCCEEDED'],
+});
+```
+
+Simple webhook listener can be built on [`express`](https://expressjs.com/) library, which can helps to create a REST server for handling webhooks:
+
+```javascript
+import express from 'express';
+import bodyParser from 'body-parser';
+import { ApifyClient, DownloadItemsFormat } from 'apify-client';
+
+// Initialize Apify client, express and define server port
+const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
+const app = express();
+const PORT = 3000;
+
+// Tell express to use body-parser's JSON parsing
+app.use(bodyParser.json());
+
+app.post('apify-webhook', async (req, res) => {
+ // Log the payload from the webhook
+ console.log(req.body);
+
+ const runDataset = await client.dataset(req.body.resource.defaultDatasetId);
+
+ // e.g. Save dataset locally as JSON
+ await runDataset.downloadItems(DownloadItemsFormat.JSON);
+
+ // Respond to the webhook
+ res.send('Webhook received');
+});
+
+// Start express on the defined port
+app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`));
+```
diff --git a/docs/03_guides/05_convenience_functions.md b/docs/03_guides/05_convenience_functions.md
new file mode 100644
index 000000000..973526224
--- /dev/null
+++ b/docs/03_guides/05_convenience_functions.md
@@ -0,0 +1,24 @@
+---
+sidebar_label: 'Convenience functions'
+title: 'Convenience functions'
+---
+
+Some actions can't be performed by the API itself, such as indefinite waiting for an Actor run to finish (because of network timeouts).
+
+The client provides convenient `call()` and `waitForFinish()` functions that do that. If the limit is reached, the returned promise is resolved to a run object that will have status `READY` or `RUNNING` and it will not contain the Actor run output.
+
+[Key-value store](https://docs.apify.com/platform/storage/key-value-store) records can be retrieved as objects, buffers, or streams via the respective options, dataset items can be fetched as individual objects or serialized data.
+
+```js
+import { ApifyClient } from 'apify-client';
+
+const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
+
+// Starts an Actor and waits for it to finish.
+const finishedActorRun = await client.actor('username/actor-name').call();
+
+// Starts an Actor and waits maximum 60s for the finish
+const { status } = await client.actor('username/actor-name').start({
+ waitForFinish: 60, // 1 minute
+});
+```
diff --git a/docs/examples/index.md b/docs/examples/index.md
deleted file mode 100644
index bcbdf24f5..000000000
--- a/docs/examples/index.md
+++ /dev/null
@@ -1,150 +0,0 @@
----
-sidebar_label: Examples
-title: 'Code examples'
----
-
-## Passing an input to the Actor
-
-The fastest way to get results from an Actor is to pass input directly to the `call` function.
-Input can be passed to `call` function and the reference of running Actor (or wait for finish) is available in `runData` variable.
-
-This example starts an Actor that scrapes 20 posts from the Instagram website based on the hashtag.
-
-```javascript
-import { ApifyClient } from 'apify-client';
-
-// Client initialization with the API token
-const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
-
-const actorClient = client.actor('apify/instagram-hashtag-scraper');
-
-const input = { hashtags: ['rainbow'], resultsLimit: 20 };
-
-// Run the Actor and wait for it to finish up to 60 seconds.
-// Input is not persisted for next runs.
-const runData = await actorClient.call(input, { waitSecs: 60 });
-
-console.log('Run data:');
-console.log(runData);
-```
-
-To run multiple inputs with the same Actor, most convenient way is to create multiple [tasks](https://docs.apify.com/platform/actors/running/tasks) with different inputs. Task input is persisted on Apify platform when task is created.
-
-```javascript
-import { ApifyClient } from 'apify-client';
-
-// Client initialization with the API token
-const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
-
-const animalsHashtags = ['zebra', 'lion', 'hippo'];
-
-// Multiple input schemas for one Actor can be persisted in tasks.
-// Tasks are saved in the Apify platform and can be run multiple times.
-const socialsTasksPromises = animalsHashtags.map((hashtag) =>
- client.tasks().create({
- actId: 'apify/instagram-hashtag-scraper',
- name: `hashtags-${hashtag}`,
- input: { hashtags: [hashtag], resultsLimit: 20 },
- options: { memoryMbytes: 1024 },
- }),
-);
-
-// Create all tasks in parallel
-const createdTasks = await Promise.all(socialsTasksPromises);
-
-console.log('Created tasks:');
-console.log(createdTasks);
-
-// Run all tasks in parallel
-await Promise.all(createdTasks.map((task) => client.task(task.id).call()));
-```
-
-## Getting latest data from an Actor, joining datasets
-
-Actor data are stored to [datasets](https://docs.apify.com/platform/storage/dataset). Datasets can be retrieved from Actor runs. Dataset items can be listed with pagination. Also, datasets can be merged together to make analysis further on with single file as dataset can be exported to various data format (CSV, JSON, XSLX, XML). [Integrations](https://docs.apify.com/platform/integrations) can do the trick as well.
-
-```javascript
-import { ApifyClient } from 'apify-client';
-
-// Client initialization with the API token
-const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
-
-const actorClient = client.actor('apify/instagram-hashtag-scraper');
-
-const actorRuns = actorClient.runs();
-
-// See pagination to understand how to get more datasets
-const actorDatasets = await actorRuns.list({ limit: 20 });
-
-console.log('Actor datasets:');
-console.log(actorDatasets);
-
-const mergingDataset = await client.datasets().getOrCreate('merge-dataset');
-
-for (const datasetItem of actorDatasets.items) {
- // Dataset items can be handled here. Dataset items can be paginated
- const datasetItems = await client.dataset(datasetItem.defaultDatasetId).listItems({ limit: 1000 });
-
- // Items can be pushed to single dataset
- await client.dataset(mergingDataset.id).pushItems(datasetItems.items);
-
- // ...
-}
-```
-
-## Handling webhooks
-
-[Webhooks](https://docs.apify.com/platform/integrations/webhooks) can be used to get notifications about Actor runs.
-For example, a webhook can be triggered when an Actor run finishes successfully.
-Webhook can receive dataset ID for further processing.
-
-Initialization of webhook:
-
-```javascript
-import { ApifyClient } from 'apify-client';
-
-// Client initialization with the API token
-const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
-
-const webhooksClient = client.webhooks();
-
-await webhooksClient.create({
- description: 'Instagram hashtag actor succeeded',
- condition: { actorId: 'reGe1ST3OBgYZSsZJ' }, // Actor ID of apify/instagram-hashtag-scraper
- // Request URL can be generated using https://webhook.site. Any REST server can be used
- requestUrl: 'https://webhook.site/CUSTOM_WEBHOOK_ID',
- eventTypes: ['ACTOR.RUN.SUCCEEDED'],
-});
-```
-
-Simple webhook listener can be built on [`express`](https://expressjs.com/) library, which can helps to create a REST server for handling webhooks:
-
-```javascript
-import express from 'express';
-import bodyParser from 'body-parser';
-import { ApifyClient, DownloadItemsFormat } from 'apify-client';
-
-// Initialize Apify client, express and define server port
-const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
-const app = express();
-const PORT = 3000;
-
-// Tell express to use body-parser's JSON parsing
-app.use(bodyParser.json());
-
-app.post('apify-webhook', async (req, res) => {
- // Log the payload from the webhook
- console.log(req.body);
-
- const runDataset = await client.dataset(req.body.resource.defaultDatasetId);
-
- // e.g. Save dataset locally as JSON
- await runDataset.downloadItems(DownloadItemsFormat.JSON);
-
- // Respond to the webhook
- res.send('Webhook received');
-});
-
-// Start express on the defined port
-app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`));
-```
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index 2324a8bed..000000000
--- a/docs/index.md
+++ /dev/null
@@ -1,283 +0,0 @@
----
-sidebar_label: 'Getting started'
-title: 'Apify API client for JavaScript'
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-`apify-client` is the official library to access the [Apify REST API](https://docs.apify.com/api/v2) from your JavaScript/TypeScript applications. It runs both in Node.js and browser and provides useful features like automatic retries and convenience functions that improve the experience of using the Apify API. All requests and responses (including errors) are encoded in JSON format with UTF-8 encoding.
-
-## Pre-requisites
-
-`apify-client` requires Node.js version 16 or higher. Node.js is available for download on the [official website](https://nodejs.org/). Check for your current node version by running:
-
-```bash
-node -v
-```
-
-## Installation
-
-You can install the client via [NPM](https://www.npmjs.com/) or use any other package manager of your choice.
-
-
-
-
-```bash
-npm i apify-client
-```
-
-
-
-
-```bash
-yarn add apify-client
-```
-
-
-
-
-```bash
-pnpm add apify-client
-```
-
-
-
-
-```bash
-bun add apify-client
-```
-
-
-
-
-## Authentication and Initialization
-
-To use the client, you need an [API token](https://docs.apify.com/platform/integrations/api#api-token). You can find your token under [Integrations](https://console.apify.com/account/integrations) tab in Apify Console. Copy the token and initialize the client by providing the token (`MY-APIFY-TOKEN`) as a parameter to the `ApifyClient` constructor.
-
-```js
-// import Apify client
-import { ApifyClient } from 'apify-client';
-
-// Client initialization with the API token
-const client = new ApifyClient({
- token: 'MY-APIFY-TOKEN',
-});
-```
-
-:::warning Secure access
-
-The API token is used to authorize your requests to the Apify API. You can be charged for the usage of the underlying services, so do not share your API token with untrusted parties or expose it on the client side of your applications
-
-:::
-
-## Quick start
-
-One of the most common use cases is starting [Actors](https://docs.apify.com/platform/actors) (serverless programs running in the [Apify cloud](https://docs.apify.com/platform)) and getting results from their [datasets](https://docs.apify.com/platform/storage/dataset) (storage) after they finish the job (usually scraping, automation processes or data processing).
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Starts an Actor and waits for it to finish
-const { defaultDatasetId } = await client.actor('username/actor-name').call();
-
-// Lists items from the Actor's dataset
-const { items } = await client.dataset(defaultDatasetId).listItems();
-```
-
-### Running Actors
-
-To start an Actor, you can use the [ActorClient](/reference/class/ActorClient) (`client.actor()`) and pass the Actor's ID (e.g. `john-doe/my-cool-actor`) to define which Actor you want to run. The Actor's ID is a combination of the username and the Actor owner’s username. You can run both your own Actors and [Actors from Apify Store](https://docs.apify.com/platform/actors/running/actors-in-store).
-
-#### Passing input to the Actor
-
-To define the Actor's input, you can pass an object to the [`call()`](/reference/class/ActorClient#call) method. The input object can be any JSON object that the Actor expects (respects the Actor's [input schema](https://docs.apify.com/platform/actors/development/actor-definition/input-schema)). The input object is used to pass configuration to the Actor, such as URLs to scrape, search terms, or any other data.
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Runs an Actor with an input and waits for it to finish.
-const { defaultDatasetId } = await client.actor('username/actor-name').call({
- some: 'input',
-});
-```
-
-### Getting results from the dataset
-
-To get the results from the dataset, you can use the [DatasetClient](/reference/class/DatasetClient) (`client.dataset()`) and [`listItems()`](/reference/class/DatasetClient#listItems) method. You need to pass the dataset ID to define which dataset you want to access. You can get the dataset ID from the Actor's run object (represented by `defaultDatasetId`).
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Lists items from the Actor's dataset.
-const { items } = await client.dataset('dataset-id').listItems();
-```
-
-:::note Dataset access
-
-Running an Actor might take time, depending on the Actor's complexity and the amount of data it processes. If you want only to get data and have an immediate response you should access the existing dataset of the finished [Actor run](https://docs.apify.com/platform/actors/running/runs-and-builds#runs).
-
-:::
-
-## Usage concepts
-
-The `ApifyClient` interface follows a generic pattern that applies to all of its components. By calling individual methods of `ApifyClient`, specific clients that target individual API resources are created. There are two types of those clients:
-
-- [`actorClient`](/reference/class/ActorClient): a client for the management of a single resource
-- [`actorCollectionClient`](/reference/class/ActorCollectionClient): a client for the collection of resources
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Collection clients do not require a parameter.
-const actorCollectionClient = client.actors();
-// Creates an actor with the name: my-actor.
-const myActor = await actorCollectionClient.create({ name: 'my-actor-name' });
-// List all your used Actors (both own and from Apify Store)
-const { items } = await actorCollectionClient.list();
-```
-
-:::note Resource identification
-
-The resource ID can be either the `id` of the said resource, or a combination of your `username/resource-name`.
-
-:::
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Resource clients accept an ID of the resource.
-const actorClient = client.actor('username/actor-name');
-// Fetches the john-doe/my-actor object from the API.
-const myActor = await actorClient.get();
-// Starts the run of john-doe/my-actor and returns the Run object.
-const myActorRun = await actorClient.start();
-```
-
-### Nested clients
-
-Sometimes clients return other clients. That's to simplify working with nested collections, such as runs of a given Actor.
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-const actorClient = client.actor('username/actor-name');
-const runsClient = actorClient.runs();
-// Lists the last 10 runs of your Actor.
-const { items } = await runsClient.list({
- limit: 10,
- desc: true,
-});
-
-// Select the last run of your Actor that finished
-// with a SUCCEEDED status.
-const lastSucceededRunClient = actorClient.lastRun({ status: 'SUCCEEDED' });
-// Fetches items from the run's dataset.
-const { items } = await lastSucceededRunClient.dataset().listItems();
-```
-
-The quick access to `dataset` and other storage directly from the run client can be used with the [`lastRun()`](/reference/class/ActorClient#lastRun) method.
-
-## Features
-
-Based on the endpoint, the client automatically extracts the relevant data and returns it in the expected format. Date strings are automatically converted to `Date` objects. For exceptions, the client throws an [`ApifyApiError`](/reference/class/ApifyApiError), which wraps the plain JSON errors returned by API and enriches them with other contexts for easier debugging.
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-try {
- const { items } = await client.dataset('non-existing-dataset-id').listItems();
-} catch (error) {
- // The error is an instance of ApifyApiError
- const { message, type, statusCode, clientMethod, path } = error;
- // Log error for easier debugging
- console.log({ message, statusCode, clientMethod, type });
-}
-```
-
-### Retries with exponential backoff
-
-Network communication sometimes fails. That's a given. The client will automatically retry requests that failed due to a network error, an internal error of the Apify API (HTTP 500+), or a rate limit error (HTTP 429). By default, it will retry up to 8 times. The first retry will be attempted after ~500ms, the second after ~1000ms, and so on. You can configure those parameters using the `maxRetries` and `minDelayBetweenRetriesMillis` options of the `ApifyClient` constructor.
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({
- token: 'MY-APIFY-TOKEN',
- maxRetries: 8,
- minDelayBetweenRetriesMillis: 500, // 0.5s
- timeoutSecs: 360, // 6 mins
-});
-```
-
-### Convenience functions and options
-
-Some actions can't be performed by the API itself, such as indefinite waiting for an Actor run to finish (because of network timeouts). The client provides convenient `call()` and `waitForFinish()` functions that do that. If the limit is reached, the returned promise is resolved to a run object that will have status `READY` or `RUNNING` and it will not contain the Actor run output.
-
-[Key-value store](https://docs.apify.com/platform/storage/key-value-store) records can be retrieved as objects, buffers, or streams via the respective options, dataset items can be fetched as individual objects or serialized data.
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Starts an Actor and waits for it to finish.
-const finishedActorRun = await client.actor('username/actor-name').call();
-
-// Starts an Actor and waits maximum 60s for the finish
-const { status } = await client.actor('username/actor-name').start({
- waitForFinish: 60, // 1 minute
-});
-```
-
-### Pagination
-
-Most methods named `list` or `listSomething` return a [`Promise`](/reference/interface/PaginatedList). There are some exceptions though, like `listKeys` or `listHead` which paginate differently. The results you're looking for are always stored under `items` and you can use the `limit` property to get only a subset of results. Other props are also available, depending on the method.
-
-```js
-import { ApifyClient } from 'apify-client';
-
-const client = new ApifyClient({ token: 'MY-APIFY-TOKEN' });
-
-// Resource clients accept an ID of the resource.
-const datasetClient = client.dataset('dataset-id');
-
-// Number of items per page
-const limit = 1000;
-// Initial offset
-let offset = 0;
-// Array to store all items
-let allItems = [];
-
-while (true) {
- const { items, total } = await datasetClient.listItems({ limit, offset });
-
- console.log(`Fetched ${items.length} items`);
-
- // Merge new items with other already loaded items
- allItems.push(...items);
-
- // If there are no more items to fetch, exit the loading
- if (offset + limit >= total) {
- break;
- }
-
- offset += limit;
-}
-
-console.log(`Overall fetched ${allItems.length} items`);
-```
diff --git a/website/sidebars.js b/website/sidebars.js
index 3dacf339d..c5b96832c 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -2,8 +2,36 @@ module.exports = [
[
{
type: 'category',
- label: 'API Client',
- items: ['index', 'examples/index'],
+ label: 'Introduction',
+ items: [
+ '01_introduction/index',
+ '01_introduction/installation',
+ '01_introduction/quick-start',
+ ],
},
+ {
+ type: 'category',
+ label: 'Concepts',
+ items: [
+ '02_concepts/01_authentication',
+ '02_concepts/02_client_architecture',
+ '02_concepts/03_error_handling',
+ '02_concepts/04_retries',
+ '02_concepts/05_pagination',
+ '02_concepts/06_nested_clients',
+ ],
+ },
+ {
+ type: 'category',
+ label: 'Guides',
+ items: [
+ '03_guides/01_passing_input_to_actors',
+ '03_guides/02_retrieving_datasets',
+ '03_guides/03_joining_datasets',
+ '03_guides/04_using_webhooks',
+ '03_guides/05_convenience_functions',
+ ],
+ },
+ 'changelog',
],
];