Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions .github/workflows/go-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ on:
- '**/*.go'
- 'go.mod'
- 'go.sum'
# The "Test examples" step validates the in-documentation snippets, so doc changes must
# re-run the workflow even though Markdown is not Go code.
- 'docs/**'
- 'README.md'
- '.github/workflows/go-integration-tests.yml'
workflow_dispatch:

Expand Down Expand Up @@ -77,5 +81,23 @@ jobs:
env:
# The integration-test token is stored as a repository secret.
APIFY_TOKEN: ${{ secrets.APIFY_TOKEN }}
# Limit parallelism to be gentle on the shared test account.
run: go test ./tests/ -v -timeout 900s -p 1 -parallel 4
# Limit parallelism to be gentle on the shared test account. The documentation example
# programs (TestExample*) and the in-documentation snippet checks (TestDocSnippets*) are
# exercised by the standalone "Test examples" step below, so they are skipped here to
# keep the two concerns separate.
run: go test ./tests/ -v -timeout 900s -p 1 -parallel 4 -skip '^(TestExample|TestDocSnippets)'

# Standalone CI step that verifies the documentation examples actually work. It runs the
# example programs in examples/ end-to-end against the live API (via the TestExample*
# smoke tests in tests/examples_test.go, each of which executes `go run ./examples/<name>`)
# and checks that every in-documentation code snippet is valid, runnable, and gofmt-
# formatted (the TestDocSnippets* tests, which extract every ```go block from the README
# and docs/ and compile each one). Both are required by the documentation requirements:
# each documentation example has a CI test that actually runs the code, and each snippet
# must be valid, runnable and properly formatted.
- name: Test examples
env:
APIFY_TOKEN: ${{ secrets.APIFY_TOKEN }}
# Match the integration-test thread cap so the example programs (which hit the live
# account) stay gentle on the shared test account.
run: go test ./tests/ -v -timeout 900s -p 1 -parallel 4 -run '^(TestExample|TestDocSnippets)'
79 changes: 79 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,85 @@ All notable changes to the Apify Go client are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.1] - 2026-06-19

Documentation and CI compliance with the updated client requirements. No changes to the
public API or to the OpenAPI spec version (`v2-2026-06-18T095846Z`), so there are no breaking
changes.

### Added

- CI: a standalone `Test examples` step in the Go integration workflow that actually runs the
documentation example code end-to-end. It executes the example programs in `examples/` against
the live API (the `TestExample*` smoke tests, each running `go run ./examples/<name>`) and
validates that every in-documentation `go` snippet is valid, runnable, and gofmt-formatted
(the new `TestDocSnippets*` tests, which extract every ```go block from the README and `docs/`
and compile each one). The `Integration tests` step now skips these so the two concerns stay
separate, mirroring the Rust sibling client.
- Tests: `tests/docs_snippets_test.go`, an offline doc-snippet harness (Go has no Markdown
doctest equivalent) that enforces the requirement that each in-documentation code snippet is
valid, runnable, and properly formatted.

### Changed

- Workflow now also triggers on `docs/**` and `README.md` changes, so documentation edits
re-run the snippet validation.

### Fixed

- Reformatted all `docs/` and README code snippets to canonical gofmt output (one-line
`if err != nil { ... }` blocks expanded, tab indentation, aligned trailing comments) and made
the custom-HTTP-transport snippet a complete, compilable program.
- Corrected the README versioning note, which referenced the older spec version
`v2-2026-06-16T064758Z` instead of the current `v2-2026-06-18T095846Z`.

### Documentation

- Documented the shared `ListOptions` type (fields + example) in `docs/README.md`, which several
resource pages reference as a method argument but which was previously undefined in the docs.
- Documented the `apify.ListDatasetItems[T]` generic helper's argument types and added a runnable
typed-decoding example in `docs/storages.md`.
- Added explicit field listings for the `ActorRun`, `Build`, `User`, and `ActorStoreListItem`
response models to `docs/runs.md`, `docs/builds.md`, and `docs/misc.md`.
- Added full field listings in `docs/storages.md` for the storage option/parameter structs that
were previously named in method tables but not enumerated: `DatasetListItemsOptions`,
`DatasetDownloadOptions`, `ListKeysOptions`, `GetRecordOptions`, `GetRecordsOptions`,
`ListRequestsOptions`, and the `RequestQueueRequest` payload.
- Documented the storage *return* types that examples dereference: `KeyValueStoreRecord`,
`KeyValueStoreKeysPage` (and `KeyValueStoreKey`), `RequestQueueHead`,
`RequestQueueOperationInfo`, and `BatchAddResult` in `docs/storages.md`.
- Added field tables for the remaining response models (`Actor`, `Task`, `Schedule`, `Webhook`,
`WebhookDispatch`) to their resource pages, matching the treatment of `ActorRun`/`Build`.
- Documented the accepted values / details of the enum-like parameters `RunListOptions.Status`,
`ListRequestsOptions.Filter` (`"locked"`/`"pending"`), `DatasetListItemsOptions.View`, and the
full `StorageListOptions` field table; and clarified in `docs/README.md` that the
within-storage listers (`ListKeys`, `ListHead`) return their own page/head containers rather
than `PaginationList[T]`.
- Expanded the run/Actor/store *input* option structs from bare name lists into full
field/type/meaning tables: `ActorStartOptions` (including the nested ad-hoc `Webhooks` element
shape and `ForcePermissionLevel`), `ActorBuildOptions`, `ActorListOptions`, `StoreListOptions`
(with enum values for `SortBy`/`PricingModel`), `RunResurrectOptions`, `MetamorphOptions`, and
`LogOptions`; and made `StorageListOptions.Ownership` state its accepted values definitively.
- Documented the schedule `actions` payload shape with a runnable `RUN_ACTOR` action example in
`docs/schedules.md`, replacing the empty `[]any{}` placeholder.
- Stated the closed enum sets definitively (verified against the OpenAPI spec) instead of hedging
with "e.g.": `StoreListOptions.PricingModel` (`FREE`, `FLAT_PRICE_PER_MONTH`,
`PRICE_PER_DATASET_ITEM`, `PAY_PER_EVENT` — previously omitted `PAY_PER_EVENT`),
`StoreListOptions.ResponseFormat` (`full`, `agent` — previously unspecified),
`ActorStartOptions.ForcePermissionLevel` (`LIMITED_PERMISSIONS`, `FULL_PERMISSIONS`), and
`ActorListOptions.SortBy` (`createdAt`, `stats.lastRunStartedAt`).
- Corrected and completed the run/build status enum documentation: the canonical `ActorJobStatus`
enum has eight values, but the docs (and the `ActorRun.Status` in-code comment) listed only six
for runs and four for builds. Now `ActorRun.Status`, `RunListOptions.Status`, and `Build.Status`
all document the full set `READY`, `RUNNING`, `SUCCEEDED`, `FAILED`, `TIMING-OUT`, `TIMED-OUT`,
`ABORTING`, `ABORTED`. (Behavior unchanged: `IsTerminal` still treats only the four terminal
states as finished, which is correct.)
- Documented the closed enums in the actor/version `Create` definition (`sourceType` =
`VersionSourceType`: `SOURCE_FILES`/`GIT_REPO`/`TARBALL`/`GITHUB_GIST`/`SOURCE_CODE`; source-file
`format` = `TEXT`/`BASE64`) with a runnable `SOURCE_FILES` example in `docs/actors.md`.
- Enumerated the closed 12-value `WebhookEventType` set in `docs/webhooks.md` and cross-referenced
it from the `ActorStartOptions.Webhooks` note.

## [0.2.0] - 2026-06-19

Verified against OpenAPI specification version `v2-2026-06-18T095846Z` (bumped from
Expand Down
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ options for full control:
```go
client := apify.NewClientWithOptions(
apify.WithToken("my-api-token"),
apify.WithBaseURL("https://api.apify.com"), // /v2 is appended automatically
apify.WithMaxRetries(8), // default 8
apify.WithBaseURL("https://api.apify.com"), // /v2 is appended automatically
apify.WithMaxRetries(8), // default 8
apify.WithMinDelayBetweenRetries(500*time.Millisecond),
apify.WithTimeout(360*time.Second), // default 6 minutes
apify.WithTimeout(360*time.Second), // default 6 minutes
apify.WithUserAgentSuffix("MyTool/1.0"),
apify.WithHTTPBackend(apify.NewDefaultHTTPBackend()),
)
Expand Down Expand Up @@ -135,23 +135,35 @@ The transport is replaceable. Implement `HTTPBackend` (a single `Do` method) to
custom client, proxy, or test double, and pass it with `WithHTTPBackend`:

```go
package main

import (
"net/http"

apify "github.com/apify/apify-client-go"
)

// myBackend is a custom HTTPBackend wrapping a standard *http.Client.
type myBackend struct{ inner *http.Client }

func (b *myBackend) Do(req *http.Request) (*http.Response, error) {
return b.inner.Do(req)
}

client := apify.NewClientWithOptions(
apify.WithToken("my-api-token"),
apify.WithHTTPBackend(&myBackend{inner: http.DefaultClient}),
)
func main() {
client := apify.NewClientWithOptions(
apify.WithToken("my-api-token"),
apify.WithHTTPBackend(&myBackend{inner: http.DefaultClient}),
)
_ = client
}
```

## Versioning

- `apify.CLIENT_VERSION` — the semantic version of this library.
- `apify.API_SPEC_VERSION` — the Apify OpenAPI spec version this client was built against
(`v2-2026-06-16T064758Z`).
(`v2-2026-06-18T095846Z`).

## Examples

Expand Down
36 changes: 34 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,36 @@ local:

```go
page, err := client.Actors().List(ctx, apify.ActorListOptions{
My: apify.Ptr(true),
Limit: apify.Ptr(int64(10)),
My: apify.Ptr(true),
Limit: apify.Ptr(int64(10)),
})
```

## Common list options — `apify.ListOptions`

Most `List` methods (builds, runs, tasks, schedules, webhooks, Actor versions) take the shared
`apify.ListOptions`, which carries the standard pagination/ordering controls. All fields are
optional pointers; leave a field `nil` to use the API default. Use `apify.Ptr` to set them
inline.

| Field | Type | Meaning |
|---|---|---|
| `Offset` | `*int64` | Number of items to skip from the start of the list. |
| `Limit` | `*int64` | Maximum number of items to return. |
| `Desc` | `*bool` | If `true`, return items newest-first. |

```go
page, err := client.Builds().List(ctx, apify.ListOptions{
Limit: apify.Ptr(int64(50)),
Desc: apify.Ptr(true),
})
```

Collections with extra filters use a dedicated options type instead of (or in addition to)
`ListOptions`: `ActorListOptions` (Actors), `StorageListOptions` (datasets/key-value
stores/request queues), `StoreListOptions` (the Store), and `RunListOptions` (runs, passed
alongside `ListOptions`). Each is documented on its resource page.

## Pagination

List/iterate methods return `apify.PaginationList[T]`, one page plus the API's pagination
Expand All @@ -52,6 +77,13 @@ metadata:
> immediately after a `PushItems` (or other write) `Total` may not yet include the new items.
> Re-read after a short delay if you need an exact post-write total.

The *collection* `List` methods (Actors, builds, runs, tasks, schedules, webhooks, datasets,
key-value stores, request queues) return `PaginationList[T]`. The *within-storage* listers use
their own page/head containers instead, because the underlying API endpoints paginate
differently: `KeyValueStoreClient.ListKeys` returns `KeyValueStoreKeysPage` (key-based
pagination) and `RequestQueueClient.ListHead` returns `RequestQueueHead`. Both are documented on
the [storages](storages.md) page.

## Pages

- [Actors](actors.md) — Actors, versions, environment variables.
Expand Down
88 changes: 82 additions & 6 deletions docs/actors.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,67 @@ Actors are the programs that run on the Apify platform. Access the Actor collect
| `List(ctx, ActorListOptions) (PaginationList[Actor], error)` | List the account's Actors. |
| `Create(ctx, definition any) (Actor, error)` | Create a new Actor. |

`ActorListOptions`: `Offset`, `Limit`, `Desc`, `My`, `SortBy` (all optional pointers).
`Create` takes a free-form definition (`any`) serialized to JSON, so the Actor's fields are
passed as a map (`name`, `title`, `versions`, etc.). A version's `sourceType` selects how its
source is supplied, and is one of the closed `VersionSourceType` values: `SOURCE_FILES`,
`GIT_REPO`, `TARBALL`, `GITHUB_GIST`, `SOURCE_CODE`. For `SOURCE_FILES`, each entry in
`sourceFiles` has a `format` of `TEXT` or `BASE64`. A minimal `SOURCE_FILES` Actor:

```go
actor, err := client.Actors().Create(ctx, map[string]any{
"name": "my-actor",
"title": "My Actor",
"versions": []any{
map[string]any{
"versionNumber": "0.0",
"sourceType": "SOURCE_FILES", // VersionSourceType: SOURCE_FILES|GIT_REPO|TARBALL|GITHUB_GIST|SOURCE_CODE
"sourceFiles": []any{
map[string]any{
"name": "Dockerfile",
"format": "TEXT", // SourceCodeFileFormat: TEXT|BASE64
"content": "FROM apify/actor-node:20\n",
},
},
},
},
})
if err != nil {
log.Fatal(err)
}
_ = actor
```

`ActorListOptions` (all fields optional pointers):

| Field | Type | Meaning |
|---|---|---|
| `Offset` | `*int64` | Number of Actors to skip. |
| `Limit` | `*int64` | Maximum number of Actors to return. |
| `Desc` | `*bool` | Return Actors newest-first. |
| `My` | `*bool` | Return only Actors owned by the current user. |
| `SortBy` | `*string` | Sort field. Accepted values: `createdAt`, `stats.lastRunStartedAt`. |

```go
page, err := client.Actors().List(ctx, apify.ActorListOptions{My: apify.Ptr(true), Limit: apify.Ptr(int64(10))})
```

### `Actor` fields

The `Actor` value returned by `Get`/`Create`/`Update` and listed by `List`:

| Field | Type | Meaning |
|---|---|---|
| `ID` | `string` | Unique Actor ID. |
| `UserID` | `string` | ID of the user who owns the Actor. |
| `Name` | `string` | Technical name of the Actor (used in API paths). |
| `Username` | `string` | Username of the Actor's owner. |
| `Title` | `string` | Human-readable title shown in the UI. |
| `Description` | `string` | What the Actor does. |
| `IsPublic` | `bool` | Whether the Actor is public in Apify Store. |
| `CreatedAt` | `*time.Time` | When the Actor was created. |
| `ModifiedAt` | `*time.Time` | When the Actor was last modified. |
| `Extra` | `map[string]json.RawMessage` | Any other fields returned by the API. |

## Single Actor

| Method | Description |
Expand Down Expand Up @@ -47,14 +102,35 @@ result, err := client.Actor("apify/hello-world").ValidateInputForBuild(ctx,
map[string]any{"message": "hi"},
"latest",
)
if err != nil { log.Fatal(err) }
if err != nil {
log.Fatal(err)
}
fmt.Println(string(result)) // raw JSON validation result
```

`ActorStartOptions`: `Build`, `MemoryMbytes`, `TimeoutSecs`, `WaitForFinish`, `MaxItems`,
`MaxTotalChargeUsd`, `ContentType`, `RestartOnError`, `ForcePermissionLevel`, `Webhooks`.

`ActorBuildOptions`: `BetaPackages`, `Tag`, `UseCache`, `WaitForFinish`.
`ActorStartOptions` (all fields optional):

| Field | Type | Meaning |
|---|---|---|
| `Build` | `*string` | Tag or number of the build to run (e.g. `"latest"`, `"0.1.2"`). |
| `MemoryMbytes` | `*int64` | Memory in megabytes allocated for the run. |
| `TimeoutSecs` | `*int64` | Run timeout in seconds (`0` means no timeout). |
| `WaitForFinish` | `*int64` | Max seconds to wait server-side for the run to finish (max 60). |
| `MaxItems` | `*int64` | Maximum dataset items to charge (pay-per-result Actors). |
| `MaxTotalChargeUsd` | `*float64` | Maximum total charge in USD (pay-per-event Actors). |
| `ContentType` | `*string` | Content type of the input body (default `application/json`). |
| `RestartOnError` | `*bool` | Restart the run if it fails. |
| `ForcePermissionLevel` | `*string` | Override the Actor's permission level for this run. Accepted values: `LIMITED_PERMISSIONS`, `FULL_PERMISSIONS`. |
| `Webhooks` | `[]any` | Ad-hoc webhooks to attach to this run. Each element is a map describing one webhook: `{"eventTypes": []string, "requestUrl": string, "payloadTemplate": string}` (same shape as a webhook definition; `eventTypes` are the `WebhookEventType` values listed in [webhooks.md](webhooks.md)). |

`ActorBuildOptions` (all fields optional):

| Field | Type | Meaning |
|---|---|---|
| `BetaPackages` | `*bool` | Use beta versions of Apify packages. |
| `Tag` | `*string` | Tag to apply to the build (e.g. `"latest"`). |
| `UseCache` | `*bool` | Whether to use the Docker build cache (default `true`). |
| `WaitForFinish` | `*int64` | Max seconds to wait server-side for the build (max 60). |

```go
// Start an Actor with input and wait for it to finish.
Expand Down
14 changes: 14 additions & 0 deletions docs/builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,18 @@ fmt.Println("build status:", finished.Status)
logText, ok, err := client.Build(build.ID).Log().Get(ctx)
```

### `Build` fields

The `Build` value returned by the build methods carries the build's metadata:

| Field | Type | Meaning |
|---|---|---|
| `ID` | `string` | Unique build ID. |
| `ActID` | `string` | ID of the Actor this build belongs to. |
| `Status` | `string` | Build status. One of the eight `ActorJobStatus` values (shared with runs): `READY`, `RUNNING`, `SUCCEEDED`, `FAILED`, `TIMING-OUT`, `TIMED-OUT`, `ABORTING`, `ABORTED`. |
| `StartedAt` | `*time.Time` | When the build started. |
| `FinishedAt` | `*time.Time` | When the build finished (`nil` while still building). |
| `BuildNumber` | `string` | Human-readable build number (e.g. `"0.1.2"`). |
| `Extra` | `map[string]json.RawMessage` | Any other fields returned by the API (forward compatibility). |

`Build.IsTerminal()` reports whether a build has finished.
Loading
Loading