Download the PHP package jcolombo/optmyzr-api-php without Composer

On this page you can find all versions of the php package jcolombo/optmyzr-api-php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package optmyzr-api-php

optmyzr-api-php

A PHP 8.1+ SDK / API client for the Optmyzr User Data API (v1).

Optmyzr is a PPC-management platform for Google Ads and Microsoft Advertising: optimization suggestions, alerts, quality-score tracking, workouts, and Blueprint task management. This library wraps the full published API surface — all nine documented endpoints — behind a typed entity layer, a fluent query builder, synthetic record identity, client-side rate limiting, optional caching, and raw escape hatches for anything Optmyzr adds later.


Two things to know before you start

1. The token travels in the query string, not a header. That is Optmyzr's design, not a choice this library made. It means the credential is part of every request URL, so it can reach access logs, proxies, and error trackers that this SDK has no control over. Inside its own boundary the SDK scrubs it everywhere — logs, exception messages, cache keys, cache files, var_dump() output — see Credential handling. Outside that boundary, treat request URLs as secrets.

2. This release is documentation-verified, not live-verified. Every endpoint, field, filter, and enum value here is transcribed from Optmyzr's published Swagger documentation and covered by mocked tests. None of it has been exercised against a live token yet. The synthetic identity keys in particular are inferences that may need recalibration against real data — see docs/IDENTITY.md. A green test suite here means "internally consistent with the documentation", not "known to work against the live API". tests/validate exists to close that gap in one command the day a token is available.


Table of contents

Requirements

Installation

Authentication

The Optmyzr User Data API authenticates with a User Token sent as a query parameter:

Optmyzr's documentation says only: "You will need an API Token to access this API. Contact your Optmyzr Account Manager for an API token." There is no self-service token generation, no documented expiry, and no documented scope model — so a token cannot be assumed present in any environment, and this SDK never fabricates one.

You supply the token once, to connect(), and never handle it again:

Optmyzr::connect() is a singleton per token + base URL — calling it again with the same token returns the same connection, with its warmed Guzzle client and rate-limit state. It makes no network call.

Never commit your token. Load it from an environment variable or an untracked config file. The provided .gitignore already excludes .env and optmyzrapi.config.json.

Quick start

Synthetic identity

The Optmyzr API returns no record identifiers. No endpoint returns one, none accepts one, and there is no GET /{id} anywhere. That makes ordinary work — caching a record, recognising it in a later fetch, storing it locally, diffing two snapshots — impossible without inventing an identity scheme.

This SDK derives three values per record:

Accessor Derived from Stable when a value changes?
naturalKey() the key fields, normalised, pipe-joined yes
id() prefix_ + hash of the natural key yes
fingerprint() hash over every field no — moves when anything moves

The split is the whole point. Hashing the entire row — the obvious approach — produces an identifier that changes whenever any value changes, so a suggestion whose changes count moves from 4 to 7 reads as a delete plus an insert rather than an update. Hashing only the key fields keeps identity stable while fingerprint() carries the "did anything move?" signal.

Per-resource keys, and the reasoning for each, are in docs/IDENTITY.md. Two structural points:

The keys are configuration, not code, because they are inferred from prose rather than observed in data:

A collision detector reports rows that collapse onto one id, which is the direct empirical check on those inferences:

API coverage

All 9 documented operations.

Endpoint Accessor Resource Paginated
GET /UserData/V1/GetAPIList apiList(), supports() (bare string[]) —
GET /UserData/V1/Accounts accounts() Account no
GET /UserData/V1/Metrics metrics() Metrics no
GET /UserData/V1/ActiveAlerts alerts() ActiveAlert no
GET /UserData/V1/OptimizationSuggestions suggestions() OptimizationSuggestion no
GET /UserData/V1/OptimizationHistory history() OptimizationHistory no
GET /UserData/V1/Workouts workouts() Workout no
GET /UserData/V1/WorkoutHistory workoutHistory() WorkoutHistory no
GET /UserData/V1/BlueprintTasks blueprintTasks() BlueprintTask yes

Full field-by-field reference: docs/API-REFERENCE.md.

Filter-support matrix

Reproduced here rather than only linked, because it is the single fact you most need and least can guess. Optmyzr's filter support is irregular — four endpoints are missing a filter you would expect them to have.

Endpoint accountId accountType emailId toolName date window status completedStatus page
GetAPIList — — — — — — — —
Accounts — — ✓ — — — — —
Metrics ✓ ✓ ✓ — — — — —
ActiveAlerts ✓ ✓ ✓ — — — — —
OptimizationSuggestions ✓ ✓ ✓ ✓ — — — —
OptimizationHistory ✓ ✓ ✓ ✓ ✓ startDate/endDate — — —
Workouts — — — — — — — —
WorkoutHistory ✓ — ✓ — — — ✓ —
BlueprintTasks ✓ — ✓ — ✓ dueDateStart/dueDateEnd ✓ — ✓

The bolded gaps are the traps:

The SDK encodes this in its API surface: each collection composes only the filter methods its endpoint supports, so $optmyzr->accounts()->status('Open') is a call-site error a static analyser catches, not a parameter the server silently ignores. Calling param() with an unsupported name throws UnsupportedFilterException.

Server-side vs client-side filtering

Because the matrix is irregular, the SDK is explicit about where each filter runs.

where() routes to a server parameter when one exists and falls back to a client-side comparison when it does not. explain() always tells you which happened — reach for it first when a result set is wider or narrower than expected.

Other client-side operations (the API offers no server equivalent for any of them):

Account scoping

The API denormalises accountType + accountId into seven of its nine resources and declares no relationships at all. AccountContext reconstructs the one join that matters:

You do not have to remember which endpoints accept accountType — that is the asymmetry this class exists to absorb. There is deliberately no workouts() accessor, because workouts have no account dimension.

Grouping works the same way:

An account id with no platform is legitimate — the API accepts accountId alone, and BlueprintTask rows carry no platform at all:

Pagination

Only BlueprintTasks paginates: 1-based page, a fixed size of 1000, and an out-of-range page returns 200 with zero entries rather than an error.

fetchAll() stops on the first empty or short page, on a page that adds no new records (a guard against a server ignoring the page parameter), or at a hard page cap. Calling it on any other endpoint issues one request and warns, rather than silently pretending to page.

For the eight unpaginated endpoints, Optmyzr documents nothing about whether they cap their result sets. The SDK exposes the raw row count and warns on a suspiciously round one:

Incremental sync

id() + fingerprint() give a real delta against an API with no identifiers. Your application persists two short strings per record — nothing else.

$diff->counts() gives ['added' => n, 'changed' => n, 'removed' => n, 'unchanged' => n], and $diff->upserts() is added + changed together.

Read-only enforcement

The API has no write endpoints, so the write methods exist and throw:

They throw rather than being absent because Call to undefined method save() reads like a broken SDK, while this message documents the API. create(), update(), save(), delete(), and fetch($id) all behave this way.

Enums are advisory

Optmyzr describes its enumerated fields in prose and never lists their values. Worse, its documentation names the ad platforms "AdWords" and "Bing Ads" — names Google and Microsoft retired years ago — so the live API may well return something else.

Every enum here is therefore advisory, and the raw string is authoritative:

Nothing throws on an unknown value, and an unrecognised value never blocks hydration. Enums exist for accountType, associationStatus, alertType, and workout type.

There is deliberately no enum for BlueprintTask.status or OptimizationHistory.status: Optmyzr documents neither vocabulary, so an enum would present a guess as an API contract. Read the real values from live data:

Credential handling

Because the token is a URL parameter, containment is structural rather than incidental:

This is covered by explicit tests asserting the token appears in no log line, no cache key, no cache file, no serialized request, and no error message.

Caching

Off by default. Enable it by pointing path.cache somewhere writable:

This API serves aggregated dashboard data rather than transactional records, so a 15-minute default window is safe.

Host applications can replace the backend entirely:

Since the API is read-only, nothing the SDK does can stale its own cache. For out-of-band changes — someone applied an optimization in Optmyzr's own UI — invalidate explicitly:

Rate limiting

Optmyzr publishes no rate limits. Its documentation declares only 200 OK for every operation and says nothing about throttling, quotas, or 429 behaviour.

The shipped defaults — roughly 4 requests/second, 120/minute, 3000/hour — are therefore a deliberately conservative guess, not a documented ceiling. Do not read them as API behaviour. Raise them once you have observed the real limits:

The limiter is nonetheless 429-aware: a Retry-After header (numeric seconds or HTTP-date) is honoured ahead of exponential backoff and capped by rateLimit.maxRetryAfterSeconds so a hostile value cannot park the process.

Errors and logging

The SDK does not throw for API-level failures. A failed request yields a RequestResponse with success === false, and errors dispatch through configurable handlers:

Because Optmyzr documents no error responses at all, the error parser tries the shapes an ASP.NET service actually produces (Message, ExceptionMessage, error, errors, …) before falling back to the status line, and an HTML error page is detected before JSON decoding so the warning names the real problem instead of reporting a syntax error at offset 0.

Configuration

Defaults ship in default.optmyzrapi.config.json; override any subset:

Block Keys
connection url, pathPrefix, verify, timeout
request dateFormat, epochMillisThreshold, pageSize, maxFetchAllPages
path cache, logs
enabled cache, logging
cache lifespan
rateLimit enabled, minDelayMs, safetyBuffer, maxRetries, retryDelayMs, maxRetryAfterSeconds, perMinute, perHour
log connections, requests
devMode extra warnings, identity-collision reporting
identity algo, normalizeAccountType, keys.<entity>
error enabled, handlers, logFilename, triggerPhpErrors
classMap defaultCollection, entity.<key>

Note on list values: a list override replaces wholesale rather than merging. Overriding identity.keys.account with ["accountId"] means exactly that one field — which is the behaviour you want, since a merge would silently produce a longer key and change every generated id.

devMode is worth enabling the first time you point this at real data: it turns on identity-collision reporting, envelope-shape warnings, and unknown-enum warnings.

Extending the SDK (classMap)

Every resource and collection is resolved through the classMap config, so a host application can substitute its own subclasses without touching the SDK:

Collections work the same way (EntityMap::overload('accounts', MyAccountCollection::class, 'collection')).

Raw escape hatches

For anything Optmyzr adds after this release:

Both flow through the full pipeline — auth, caching, rate limiting, error mapping — and $path is used verbatim, so it can reach outside UserData/V1.

Known API quirks

Each is handled, and each handler has a test. Full detail with reasoning in docs/API-REFERENCE.md; design decisions in OVERRIDES.md.

# Quirk Handling
G1 Date format is self-contradictory: prose says mm-dd-yy, the example is 02-17-2018 m-d-Y (the example wins), configurable
G2 timestamp is a date-time string on OptimizationHistory, an int64 epoch on WorkoutHistory per-resource types, never a global field-name rule
G3 Epoch unit (seconds vs milliseconds) never stated magnitude sniff at 1e12, configurable
G4 No error responses documented at all ASP.NET body shapes tried, HTML detected before decoding
G5 No rate limits documented conservative defaults, flagged as a guess
G6 Enum values described in prose only, using retired platform names raw string authoritative, enums advisory
G7 Credential travels in the query string structural containment + redaction
G8 Field casing inconsistent (timeStamp/timestamp, updatetime/updatedTime) property aliases
G9 Pagination documented for one endpoint only fetchAll() only there; raw count exposed elsewhere
G10 GetAPIList values not enumerated case- and punctuation-insensitive matching
G11 XML is an advertised response type Accept: application/json always sent
G12 accountId without accountType unspecified sent exactly as given; no platform invented

Examples

Runnable scripts in examples/:

File Shows
01-basic-connection.php connect, apiList(), supports(), disconnect
02-accounts-and-metrics.php account inventory, quality scores, raw vs enum accessors
03-suggestions-and-alerts.php per-account pending work via AccountContext
04-optimization-history.php date windows, attribution by user, totals
05-blueprint-tasks-pagination.php fetchAll(), open(), overdue(), page counts
06-identity-and-diffing.php id()/fingerprint(), snapshots, CollectionDiff
07-configuration-and-caching.php config overlays, cache bridging, explain(), collisions

Each reads OPTMYZR_API_TOKEN from the environment and exits cleanly when it is absent.

Development and testing

The suite is fully mocked (Guzzle MockHandler) and passes with zero credentials configured. It proves the SDK is internally consistent with Optmyzr's documentation — that the right URL is built, the right parameters are sent, the documented envelope is parsed, identity is stable, and the token never leaks. It does not prove the documentation matches reality.

Validating against the live API

Without a token it prints instructions and exits 0 (skipped, not failed), so CI stays green.

--verbose and --identity are the point: they answer the open questions this build could not — the real accountType strings, the real status vocabularies, the epoch unit, and above all whether the inferred identity keys are actually unique.

License

MIT — see LICENSE.


All versions of optmyzr-api-php with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1
ext-json Version *
guzzlehttp/guzzle Version ^7.8
adbario/php-dot-notation Version ^3.3
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package jcolombo/optmyzr-api-php contains the following files

Loading the files please wait ...