From 927092f3a2fb735a4a7f87466433a940d278db0d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 18 Mar 2026 18:04:56 +0100 Subject: [PATCH 1/3] docs: remove redundant transformDocs.js and pydoc-markdown setup The docusaurus-plugin-typedoc-api plugin handles the docspec-to-typedoc conversion internally. The external pydoc-markdown dump and transformDocs.js pipeline are unnecessary. Co-Authored-By: Claude Opus 4.6 (1M context) --- website/build_api_reference.sh | 17 -- website/docusaurus.config.js | 23 +- website/pydoc-markdown.yml | 13 -- website/transformDocs.js | 397 --------------------------------- 4 files changed, 21 insertions(+), 429 deletions(-) delete mode 100644 website/pydoc-markdown.yml delete mode 100644 website/transformDocs.js diff --git a/website/build_api_reference.sh b/website/build_api_reference.sh index e711a46a..134fd6c2 100755 --- a/website/build_api_reference.sh +++ b/website/build_api_reference.sh @@ -1,21 +1,4 @@ #!/bin/bash -# On macOS, sed requires a space between -i and '' to specify no backup should be done -# On Linux, sed requires no space between -i and '' to specify no backup should be done -sed_no_backup() { - if [[ $(uname) = "Darwin" ]]; then - sed -i '' "$@" - else - sed -i'' "$@" - fi -} - -# Create docspec dump of this package's source code through pydoc-markdown -pydoc-markdown --quiet --dump > docspec-dump.jsonl -sed_no_backup "s#${PWD}/..#REPO_ROOT_PLACEHOLDER#g" docspec-dump.jsonl - # Generate import shortcuts from the modules python generate_module_shortcuts.py - -# Transform the docpec dumps into Typedoc-compatible docs tree -node transformDocs.js diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index fc63d70b..80cec992 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -3,9 +3,29 @@ const { join, resolve } = require('node:path'); const { config } = require('@apify/docs-theme'); const { externalLinkProcessor } = require('./tools/utils/externalLink'); -const { groupSort } = require('./transformDocs.js'); const versions = require('./versions.json'); +const GROUP_ORDER = [ + 'Apify API clients', + 'HTTP clients', + 'Resource clients', + 'Errors', + 'Models', + 'Other', +]; + +const groupSort = (g1, g2) => { + const i1 = GROUP_ORDER.indexOf(g1); + const i2 = GROUP_ORDER.indexOf(g2); + // Both known – sort by defined order + if (i1 !== -1 && i2 !== -1) return i1 - i2; + // Unknown groups go after known ones + if (i1 !== -1) return -1; + if (i2 !== -1) return 1; + // Both unknown – alphabetical + return g1.localeCompare(g2); +}; + const { absoluteUrl } = config; /** @type {Partial} */ @@ -109,7 +129,6 @@ module.exports = { typedocOptions: { excludeExternals: false, }, - pathToCurrentVersionTypedocJSON: `${__dirname}/api-typedoc-generated.json`, sortSidebar: groupSort, routeBasePath: 'reference', python: true, diff --git a/website/pydoc-markdown.yml b/website/pydoc-markdown.yml deleted file mode 100644 index 9623e24e..00000000 --- a/website/pydoc-markdown.yml +++ /dev/null @@ -1,13 +0,0 @@ -loaders: - - type: python - search_path: [../src] -processors: - - type: filter - skip_empty_modules: true - - type: crossref -renderer: - type: docusaurus - docs_base_path: docs - relative_output_path: reference - relative_sidebar_path: sidebar.json - sidebar_top_level_label: null diff --git a/website/transformDocs.js b/website/transformDocs.js deleted file mode 100644 index 12994d9f..00000000 --- a/website/transformDocs.js +++ /dev/null @@ -1,397 +0,0 @@ -/* eslint-disable */ - -const fs = require('fs'); -const { spawnSync } = require('child_process'); - -const moduleShortcuts = require('./module_shortcuts.json'); - -const REPO_ROOT_PLACEHOLDER = 'REPO_ROOT_PLACEHOLDER'; - -const APIFY_CLIENT_REPO_URL = 'https://github.com/apify/apify-client-python'; -const APIFY_SDK_REPO_URL = 'https://github.com/apify/apify-sdk-python'; - -const REPO_URL_PER_PACKAGE = { - 'apify': APIFY_SDK_REPO_URL, - 'apify_client': APIFY_CLIENT_REPO_URL, -}; - -// For each package, get the installed version, and set the tag to the corresponding version -const TAG_PER_PACKAGE = {}; -for (const pkg of ['apify', 'apify_client']) { - const spawnResult = spawnSync('python', ['-c', `import ${pkg}; print(${pkg}.__version__)`]); - if (spawnResult.status === 0) { - TAG_PER_PACKAGE[pkg] = `v${spawnResult.stdout.toString().trim()}`; - } -} - -// For the current package, set the tag to 'master' -const thisPackagePyprojectToml = fs.readFileSync('../pyproject.toml', 'utf8'); -const thisPackageName = thisPackagePyprojectToml.match(/^name = "(.+)"$/m)[1]; -TAG_PER_PACKAGE[thisPackageName] = 'master'; - - -// Taken from https://github.com/TypeStrong/typedoc/blob/v0.23.24/src/lib/models/reflections/kind.ts, modified -const TYPEDOC_KINDS = { - 'class': { - kind: 128, - kindString: 'Class', - }, - 'function': { - kind: 2048, - kindString: 'Method', - }, - 'data': { - kind: 1024, - kindString: 'Property', - }, - 'enum': { - kind: 8, - kindString: 'Enumeration', - }, - 'enumValue': { - kind: 16, - kindString: 'Enumeration Member', - }, -} - -const GROUP_ORDER = [ - 'Apify API clients', - 'HTTP clients', - 'Resource clients', - 'Errors', - 'Models', - 'Other', -]; - -const groupSort = (g1, g2) => { - const i1 = GROUP_ORDER.indexOf(g1); - const i2 = GROUP_ORDER.indexOf(g2); - // Both known – sort by defined order - if (i1 !== -1 && i2 !== -1) return i1 - i2; - // Unknown groups go after known ones - if (i1 !== -1) return -1; - if (i2 !== -1) return 1; - // Both unknown – alphabetical - return g1.localeCompare(g2); -}; - -// Extract the docs_group name from a docspec member's decorators -function getDocsGroup(member) { - const decoration = member.decorations?.find(d => d.name === 'docs_group'); - if (decoration?.args) { - const match = decoration.args.match(/['"](.+?)['"]/); - if (match) return match[1]; - } - return undefined; -} - -function getGroupName(object) { - // If a docs_group was extracted from the Python decorator, use it - if (object.docsGroup) { - return object.docsGroup; - } - - // Fallback grouping for sub-members (methods, properties, etc.) inside classes - const groupPredicates = { - 'Methods': (x) => x.kindString === 'Method', - 'Constructors': (x) => x.kindString === 'Constructor', - 'Properties': (x) => x.kindString === 'Property', - 'Constants': (x) => x.kindString === 'Enumeration', - 'Enumeration Members': (x) => x.kindString === 'Enumeration Member', - }; - - const found = Object.entries(groupPredicates).find( - ([_, predicate]) => predicate(object) - ); - - return found ? found[0] : 'Other'; -} - -// Strips the Optional[] type from the type string, and replaces generic types with just the main type -function getBaseType(type) { - return type?.replace(/Optional\[(.*)\]/g, '$1').replace('ListPage[Dict]', 'ListPage'); -} - -// Returns whether a type is a custom class, or a primitive type -function isCustomClass(type) { - return !['dict', 'list', 'str', 'int', 'float', 'bool'].includes(type.toLowerCase()); -} - -// Infer the Typedoc type from the docspec type -function inferTypedocType(docspecType) { - const typeWithoutOptional = getBaseType(docspecType); - if (!typeWithoutOptional) { - return undefined; - } - - // Typically, if a type is a custom class, it will be a reference in Typedoc - return isCustomClass(typeWithoutOptional) ? { - type: 'reference', - name: docspecType - } : { - type: 'intrinsic', - name: docspecType, - } -} - -// Sorts the groups of a Typedoc member, and sorts the children of each group -function sortChildren(typedocMember) { - for (let group of typedocMember.groups) { - group.children - .sort((a, b) => { - const firstName = typedocMember.children.find(x => x.id === a).name; - const secondName = typedocMember.children.find(x => x.id === b).name; - return firstName.localeCompare(secondName); - }); - } - typedocMember.groups.sort((a, b) => groupSort(a.title, b.title)); -} - -// Parses the arguments and return value description of a method from its docstring -function extractArgsAndReturns(docstring) { - const parameters = (docstring - .split('Args:')[1] ?? '').split('Returns:')[0] // Get the part between Args: and Returns: - .split(/(^|\n)\s*([\w]+)\s*\(.*?\)\s*:\s*/) // Magic regex which splits the arguments into an array, and removes the argument types - .filter(x => x.length > 1) // Remove empty strings - .reduce((acc, curr, idx, arr) => { // Collect the argument names and types into an object - if(idx % 2 === 0){ - return {...acc, [curr]: arr[idx+1]} // If the index is even, the current string is an argument name, and the next string is its type - } - return acc; - }, {}); - - const returns = (docstring - .split('Returns:')[1] ?? '').split('Raises:')[0] // Get the part between Returns: and Raises: - .split(':')[1]?.trim() || undefined; // Split the return value into its type and description, return description - - - return { parameters, returns }; -} - -// Objects with decorators named 'ignore_docs' or without docstrings will be ignored, -// unless they have a 'docs_group' decorator which explicitly marks them for documentation -function isHidden(member) { - if (member.decorations?.some(d => d.name === 'ignore_docs') || member.name === 'ignore_docs') { - return true; - } - // Members with @docs_group are always visible (even without docstrings) - if (member.decorations?.some(d => d.name === 'docs_group')) { - return false; - } - return !member.docstring?.content; -} - -// Each object in the Typedoc structure has an unique ID, -// we'll just increment it for each object we convert -let oid = 1; - -// Converts a docspec object to a Typedoc object, including all its children -function convertObject(obj, parent, module) { - const rootModuleName = module.name.split('.')[0]; - for (let member of obj.members ?? []) { - let typedocKind = TYPEDOC_KINDS[member.type]; - - if(member.bases?.includes('Enum')) { - typedocKind = TYPEDOC_KINDS['enum']; - } - - if (member.decorations?.some(d => d.name === 'dualproperty')) { - typedocKind = TYPEDOC_KINDS['data']; - } - - let typedocType = inferTypedocType(member.datatype); - - if(parent.kindString === 'Enumeration') { - typedocKind = TYPEDOC_KINDS['enumValue']; - typedocType = { - type: 'literal', - value: member.value, - } - } - - if(member.type in TYPEDOC_KINDS && !isHidden(member)) { - // Get the URL of the member in GitHub - const repoBaseUrl = `${REPO_URL_PER_PACKAGE[rootModuleName]}/blob/${TAG_PER_PACKAGE[rootModuleName]}`; - const filePathInRepo = member.location.filename.replace(REPO_ROOT_PLACEHOLDER, ''); - const fileGitHubUrl = member.location.filename.replace(REPO_ROOT_PLACEHOLDER, repoBaseUrl); - const memberGitHubUrl = `${fileGitHubUrl}#L${member.location.lineno}`; - - // Get the module name of the member, and check if it has a shortcut (reexport from an ancestor module) - const fullName = `${module.name}.${member.name}`; - let moduleName = module.name; - if (fullName in moduleShortcuts) { - moduleName = moduleShortcuts[fullName].replace(`.${member.name}`, ''); - } - - // Extract docs_group from the Python decorator (if present) - const docsGroup = getDocsGroup(member); - - // Create the Typedoc member object - let typedocMember = { - id: oid++, - name: member.name, - module: moduleName, // This is an extension to the original Typedoc structure, to support showing where the member is exported from - docsGroup, - ...typedocKind, - flags: {}, - comment: member.docstring ? { - summary: [{ - kind: 'text', - text: member.docstring?.content, - }], - } : undefined, - type: typedocType, - children: [], - groups: [], - sources: [{ - filename: filePathInRepo, - line: member.location.lineno, - character: 1, - url: memberGitHubUrl, - }], - }; - - if(typedocMember.kindString === 'Method') { - const { parameters, returns } = extractArgsAndReturns(member.docstring?.content ?? ''); - - typedocMember.signatures = [{ - id: oid++, - name: member.name, - modifiers: member.modifiers ?? [], - kind: 4096, - kindString: 'Call signature', - flags: {}, - comment: member.docstring ? { - summary: [{ - kind: 'text', - text: member.docstring?.content - .replace(/\**(Args|Arguments|Returns)[\s\S]+/, ''), - }], - blockTags: returns ? [ - { tag: '@returns', content: [{ kind: 'text', text: returns }] }, - ] : undefined, - } : undefined, - type: inferTypedocType(member.return_type), - parameters: member.args.filter((arg) => (arg.name !== 'self' && arg.name !== 'cls')).map((arg) => ({ - id: oid++, - name: arg.name, - kind: 32768, - kindString: 'Parameter', - flags: { - isOptional: arg.datatype?.includes('Optional') ? 'true' : undefined, - 'keyword-only': arg.type === 'KEYWORD_ONLY' ? 'true' : undefined, - }, - type: inferTypedocType(arg.datatype), - comment: parameters[arg.name] ? { - summary: [{ - kind: 'text', - text: parameters[arg.name] - }] - } : undefined, - defaultValue: arg.default_value, - })), - }]; - } - - if(typedocMember.name === '__init__') { - typedocMember.kind = 512; - typedocMember.kindString = 'Constructor'; - } - - convertObject(member, typedocMember, module); - - const groupName = getGroupName(typedocMember); - - const group = parent.groups.find((g) => g.title === groupName); - if (group) { - group.children.push(typedocMember.id); - } else { - parent.groups.push({ - title: groupName, - children: [typedocMember.id], - }); - } - - sortChildren(typedocMember); - parent.children.push(typedocMember); - } - } -} - -function main() { - // Root object of the Typedoc structure - const typedocApiReference = { - 'id': 0, - 'name': 'apify-client', - 'kind': 1, - 'kindString': 'Project', - 'flags': {}, - 'originalName': '', - 'children': [], - 'groups': [], - 'sources': [ - { - 'fileName': 'src/index.ts', - 'line': 1, - 'character': 0, - 'url': `http://example.com/blob/123456/src/dummy.py`, - } - ] - }; - - // Load the docspec dump file of this module - const thisPackageDocspecDump = fs.readFileSync('docspec-dump.jsonl', 'utf8'); - const thisPackageModules = thisPackageDocspecDump.split('\n').filter((line) => line !== ''); - - // Convert all the modules, store them in the root object - for (const module of thisPackageModules) { - const parsedModule = JSON.parse(module); - convertObject(parsedModule, typedocApiReference, parsedModule); - }; - - // Recursively fix references (collect names->ids of all the named entities and then inject those in the reference objects) - const namesToIds = {}; - function collectIds(obj) { - for (const child of obj.children ?? []) { - namesToIds[child.name] = child.id; - collectIds(child); - } - } - collectIds(typedocApiReference); - - function fixRefs(obj) { - for (const child of obj.children ?? []) { - if (child.type?.type === 'reference') { - child.type.id = namesToIds[child.type.name]; - } - if (child.signatures) { - for (const sig of child.signatures) { - for (const param of sig.parameters ?? []) { - if (param.type?.type === 'reference') { - param.type.id = namesToIds[param.type.name]; - } - } - if (sig.type?.type === 'reference') { - sig.type.id = namesToIds[sig.type.name]; - } - } - } - fixRefs(child); - } - } - fixRefs(typedocApiReference); - - // Sort the children of the root object - sortChildren(typedocApiReference); - - // Write the Typedoc structure to the output file - fs.writeFileSync('./api-typedoc-generated.json', JSON.stringify(typedocApiReference, null, 4)); -} - -if (require.main === module) { - main(); -} - -module.exports = { - groupSort, -} From 64e45f5924078f75e7cbdee1592654723216589d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 18 Mar 2026 20:30:27 +0100 Subject: [PATCH 2/3] docs: restructure versioned docs sidebar and add version-aware navbar Unify sidebar structure across all doc versions to: Overview, Quick start, Concepts, Examples/Guides, Upgrading, Changelog. For v1.12, merge the multi-page Overview section into a single Overview page and add a dedicated Quick start page. Rename directory from overview to introduction for consistent doc IDs across versions. Add custom VersionedReferenceNavbarItem component so the Reference tab follows the selected docs version. Make Docs and Changelog tabs version-aware using Docusaurus type: 'doc' navbar items. Co-Authored-By: Claude Opus 4.6 (1M context) --- website/docusaurus.config.js | 8 ++- .../src/theme/NavbarItem/ComponentTypes.js | 7 ++ .../VersionedReferenceNavbarItem.js | 16 +++++ .../code/01_usage_async.py | 0 .../code/01_usage_sync.py | 0 .../code/02_auth_async.py | 0 .../code/02_auth_sync.py | 0 .../code/03_dataset_async.py | 0 .../code/03_dataset_sync.py | 0 .../code/03_input_async.py | 0 .../code/03_input_sync.py | 0 .../index.mdx} | 27 ++++++- .../quick-start.mdx} | 67 ++++++++++++++--- .../01_overview/02_setting_up.mdx | 71 ------------------- .../version-1.12-sidebars.json | 19 ++--- 15 files changed, 118 insertions(+), 97 deletions(-) create mode 100644 website/src/theme/NavbarItem/ComponentTypes.js create mode 100644 website/src/theme/NavbarItem/VersionedReferenceNavbarItem.js rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/01_usage_async.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/01_usage_sync.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/02_auth_async.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/02_auth_sync.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/03_dataset_async.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/03_dataset_sync.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/03_input_async.py (100%) rename website/versioned_docs/version-1.12/{01_overview => 01_introduction}/code/03_input_sync.py (100%) rename website/versioned_docs/version-1.12/{01_overview/01_introduction.mdx => 01_introduction/index.mdx} (55%) rename website/versioned_docs/version-1.12/{01_overview/03_getting_started.mdx => 01_introduction/quick-start.mdx} (50%) delete mode 100644 website/versioned_docs/version-1.12/01_overview/02_setting_up.mdx diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 80cec992..d6c4bae2 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -66,19 +66,21 @@ module.exports = { title: 'API Client for Python', items: [ { - to: 'docs', + type: 'doc', + docId: 'introduction/introduction', label: 'Docs', position: 'left', activeBaseRegex: '/docs(?!/changelog)', }, { - to: '/reference', + type: 'custom-versioned-reference', label: 'Reference', position: 'left', activeBaseRegex: '/reference', }, { - to: 'docs/changelog', + type: 'doc', + docId: 'changelog', label: 'Changelog', position: 'left', activeBaseRegex: '/docs/changelog', diff --git a/website/src/theme/NavbarItem/ComponentTypes.js b/website/src/theme/NavbarItem/ComponentTypes.js new file mode 100644 index 00000000..b6b4f89d --- /dev/null +++ b/website/src/theme/NavbarItem/ComponentTypes.js @@ -0,0 +1,7 @@ +import OriginalComponentTypes from '@theme-original/NavbarItem/ComponentTypes'; +import VersionedReferenceNavbarItem from './VersionedReferenceNavbarItem'; + +export default { + ...OriginalComponentTypes, + 'custom-versioned-reference': VersionedReferenceNavbarItem, +}; diff --git a/website/src/theme/NavbarItem/VersionedReferenceNavbarItem.js b/website/src/theme/NavbarItem/VersionedReferenceNavbarItem.js new file mode 100644 index 00000000..1614a4a8 --- /dev/null +++ b/website/src/theme/NavbarItem/VersionedReferenceNavbarItem.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { useDocsVersionCandidates } from '@docusaurus/plugin-content-docs/client'; +import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; + +/* eslint-disable react/prop-types */ +export default function VersionedReferenceNavbarItem({ docsPluginId, ...props }) { + const [version] = useDocsVersionCandidates(docsPluginId); + + // Latest version → /reference, "current" (next) → /reference/next, others → /reference/{name} + let to = '/reference'; + if (!version.isLast) { + to = `/reference/${version.name === 'current' ? 'next' : version.name}`; + } + + return ; +} diff --git a/website/versioned_docs/version-1.12/01_overview/code/01_usage_async.py b/website/versioned_docs/version-1.12/01_introduction/code/01_usage_async.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/01_usage_async.py rename to website/versioned_docs/version-1.12/01_introduction/code/01_usage_async.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/01_usage_sync.py b/website/versioned_docs/version-1.12/01_introduction/code/01_usage_sync.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/01_usage_sync.py rename to website/versioned_docs/version-1.12/01_introduction/code/01_usage_sync.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/02_auth_async.py b/website/versioned_docs/version-1.12/01_introduction/code/02_auth_async.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/02_auth_async.py rename to website/versioned_docs/version-1.12/01_introduction/code/02_auth_async.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/02_auth_sync.py b/website/versioned_docs/version-1.12/01_introduction/code/02_auth_sync.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/02_auth_sync.py rename to website/versioned_docs/version-1.12/01_introduction/code/02_auth_sync.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/03_dataset_async.py b/website/versioned_docs/version-1.12/01_introduction/code/03_dataset_async.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/03_dataset_async.py rename to website/versioned_docs/version-1.12/01_introduction/code/03_dataset_async.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/03_dataset_sync.py b/website/versioned_docs/version-1.12/01_introduction/code/03_dataset_sync.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/03_dataset_sync.py rename to website/versioned_docs/version-1.12/01_introduction/code/03_dataset_sync.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/03_input_async.py b/website/versioned_docs/version-1.12/01_introduction/code/03_input_async.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/03_input_async.py rename to website/versioned_docs/version-1.12/01_introduction/code/03_input_async.py diff --git a/website/versioned_docs/version-1.12/01_overview/code/03_input_sync.py b/website/versioned_docs/version-1.12/01_introduction/code/03_input_sync.py similarity index 100% rename from website/versioned_docs/version-1.12/01_overview/code/03_input_sync.py rename to website/versioned_docs/version-1.12/01_introduction/code/03_input_sync.py diff --git a/website/versioned_docs/version-1.12/01_overview/01_introduction.mdx b/website/versioned_docs/version-1.12/01_introduction/index.mdx similarity index 55% rename from website/versioned_docs/version-1.12/01_overview/01_introduction.mdx rename to website/versioned_docs/version-1.12/01_introduction/index.mdx index 0b49f85d..78f90b65 100644 --- a/website/versioned_docs/version-1.12/01_overview/01_introduction.mdx +++ b/website/versioned_docs/version-1.12/01_introduction/index.mdx @@ -1,6 +1,9 @@ --- id: introduction -title: Introduction +title: Overview +sidebar_label: Overview +slug: / +description: "The official Python library to access the Apify API, with automatic retries, async support, and comprehensive API coverage." --- import Tabs from '@theme/Tabs'; @@ -12,6 +15,26 @@ import UsageSyncExample from '!!raw-loader!./code/01_usage_sync.py'; The [Apify client for Python](https://github.com/apify/apify-client-python) is the official library to access the [Apify REST API](https://docs.apify.com/api/v2) from your Python applications. It 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. The client provides both synchronous and asynchronous interfaces. +## Prerequisites + +`apify-client` requires Python 3.10 or higher. Python is available for download on the [official website](https://www.python.org/downloads/). Check your current Python version by running: + +```bash +python --version +``` + +## Installation + +The Apify client is available as the [`apify-client`](https://pypi.org/project/apify-client/) package on PyPI. + +```bash +pip install apify-client +``` + +## Quick example + +Here's an example showing how to run an Actor and retrieve its results: + @@ -24,3 +47,5 @@ The [Apify client for Python](https://github.com/apify/apify-client-python) is t + +> You can find your API token in the [Integrations section](https://console.apify.com/account/integrations) of Apify Console. See the [Quick start guide](./quick-start) for more details on authentication. diff --git a/website/versioned_docs/version-1.12/01_overview/03_getting_started.mdx b/website/versioned_docs/version-1.12/01_introduction/quick-start.mdx similarity index 50% rename from website/versioned_docs/version-1.12/01_overview/03_getting_started.mdx rename to website/versioned_docs/version-1.12/01_introduction/quick-start.mdx index 43b128d0..6c1bad8e 100644 --- a/website/versioned_docs/version-1.12/01_overview/03_getting_started.mdx +++ b/website/versioned_docs/version-1.12/01_introduction/quick-start.mdx @@ -1,12 +1,16 @@ --- -id: getting-started -title: Getting started +id: quick-start +title: Quick start +sidebar_label: Quick start +description: "Get started with the Apify API client for Python by running an Actor and retrieving results from its dataset." --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; +import AuthAsyncExample from '!!raw-loader!./code/02_auth_async.py'; +import AuthSyncExample from '!!raw-loader!./code/02_auth_sync.py'; import UsageAsyncExample from '!!raw-loader!./code/01_usage_async.py'; import UsageSyncExample from '!!raw-loader!./code/01_usage_sync.py'; import InputAsyncExample from '!!raw-loader!./code/03_input_async.py'; @@ -14,9 +18,34 @@ import InputSyncExample from '!!raw-loader!./code/03_input_sync.py'; import DatasetAsyncExample from '!!raw-loader!./code/03_dataset_async.py'; import DatasetSyncExample from '!!raw-loader!./code/03_dataset_sync.py'; -This guide will walk you through how to use the [Apify Client for Python](https://github.com/apify/apify-client-python) to run [Actors](https://apify.com/actors) on the [Apify platform](https://docs.apify.com/platform), provide input to them, and retrieve results from their datasets. You'll learn the basics of running serverless programs (we're calling them Actors) and managing their output efficiently. +Learn how to authenticate, run Actors, and retrieve results using the Apify API client for Python. -## Running your first Actor +--- + +## Step 1: Authenticate the client + +To use the client, you need an [API token](https://docs.apify.com/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 (`MY-APIFY-TOKEN`) as a parameter to the `ApifyClient` constructor. + + + + + {AuthAsyncExample} + + + + + {AuthSyncExample} + + + + +:::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: Run an 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 username 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). @@ -33,7 +62,7 @@ To start an Actor, you need its ID (e.g., `john-doe/my-cool-actor`) and an API t -## Providing input to Actor +## Step 3: Provide input to an 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). @@ -50,19 +79,19 @@ Actors often require input, such as URLs to scrape, search terms, or other confi -## Getting results from the dataset +## Step 4: Get 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.list_items`](/reference/class/DatasetClient#list_items) 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 dictionary (represented by `defaultDatasetId`). +To get the results from the dataset, you can use the [`DatasetClient`](/reference/class/DatasetClient) ([`ApifyClient.dataset`](/reference/class/ApifyClient#dataset)) and [`DatasetClient.list_items`](/reference/class/DatasetClient#list_items) 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 dictionary (represented by `defaultDatasetId`). - {InputAsyncExample} + {DatasetAsyncExample} - {InputSyncExample} + {DatasetSyncExample} @@ -72,3 +101,23 @@ To get the results from the dataset, you can use the [`DatasetClient`](/referenc 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). ::: + +## Next steps + +### Concepts + +To learn more about how the client works, check out the Concepts section in the sidebar: + +- [Asyncio support](../concepts/asyncio-support) - asynchronous programming with the client +- [Single and collection clients](../concepts/single-and-collection-clients) - resource clients and collection clients +- [Error handling](../concepts/error-handling) - debugging API errors +- [Retries](../concepts/retries) - automatic retries with exponential backoff +- [Pagination](../concepts/pagination) - iterating through large result sets + +### Examples + +For practical examples of common tasks, see the Examples section: + +- [Pass input to an Actor](../examples/passing-input-to-actor) +- [Retrieve Actor data](../examples/retrieve-actor-data) +- [Integrate with data libraries](../examples/integration-with-data-libraries) diff --git a/website/versioned_docs/version-1.12/01_overview/02_setting_up.mdx b/website/versioned_docs/version-1.12/01_overview/02_setting_up.mdx deleted file mode 100644 index ef67a98c..00000000 --- a/website/versioned_docs/version-1.12/01_overview/02_setting_up.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -id: setting-up -title: Setting up ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import CodeBlock from '@theme/CodeBlock'; - -import AsyncExample from '!!raw-loader!./code/02_auth_async.py'; -import SyncExample from '!!raw-loader!./code/02_auth_sync.py'; - -This guide will help you get started with [Apify client for Python](https://github.com/apify/apify-client-python) by setting it up on your computer. Follow the steps below to ensure a smooth installation process. - -## Prerequisites - -Before installing `apify-client` itself, make sure that your system meets the following requirements: - -- **Python 3.10 or higher**: `apify-client` requires Python 3.10 or a newer version. You can download Python from the [official website](https://www.python.org/downloads/). -- **Python package manager**: While this guide uses Pip (the most common package manager), you can also use any package manager you want. You can download Pip from the [official website](https://pip.pypa.io/en/stable/installation/). - -### Verifying prerequisites - -To check if Python and the Pip package manager are installed, run the following commands: - -```sh -python --version -``` - -```sh -pip --version -``` - -If these commands return the respective versions, you're ready to continue. - -## Installation - -Apify client for Python is available as the [`apify-client`](https://pypi.org/project/apify-client/) package on PyPI. To install it, run: - -```sh -pip install apify-client -``` - -After installation, verify that `apify-client` is installed correctly by checking its version: - -```sh -python -c 'import apify_client; print(apify_client.__version__)' -``` - -## 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. - - - - - {AsyncExample} - - - - - {SyncExample} - - - - -:::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. - -::: diff --git a/website/versioned_sidebars/version-1.12-sidebars.json b/website/versioned_sidebars/version-1.12-sidebars.json index 84d35cb0..0c35235b 100644 --- a/website/versioned_sidebars/version-1.12-sidebars.json +++ b/website/versioned_sidebars/version-1.12-sidebars.json @@ -1,15 +1,12 @@ { "sidebar": [ { - "type": "category", - "label": "Overview", - "collapsed": false, - "items": [ - { - "type": "autogenerated", - "dirName": "01_overview" - } - ] + "type": "doc", + "id": "introduction/introduction" + }, + { + "type": "doc", + "id": "introduction/quick-start" }, { "type": "category", @@ -32,10 +29,6 @@ "dirName": "03_examples" } ] - }, - { - "type": "doc", - "id": "changelog" } ] } From 6a85cd5110a2e2cd4caa6b5817d6496bed4ce63a Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Wed, 18 Mar 2026 20:48:07 +0100 Subject: [PATCH 3/3] align structure --- website/versioned_sidebars/version-1.12-sidebars.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/versioned_sidebars/version-1.12-sidebars.json b/website/versioned_sidebars/version-1.12-sidebars.json index 0c35235b..50de8248 100644 --- a/website/versioned_sidebars/version-1.12-sidebars.json +++ b/website/versioned_sidebars/version-1.12-sidebars.json @@ -21,7 +21,7 @@ }, { "type": "category", - "label": "Examples", + "label": "Guides", "collapsed": true, "items": [ { @@ -29,6 +29,10 @@ "dirName": "03_examples" } ] + }, + { + "type": "doc", + "id": "changelog" } ] }