Download the PHP package systemverk/laravel-api-usage without Composer

On this page you can find all versions of the php package systemverk/laravel-api-usage. 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 laravel-api-usage

Laravel API Usage

CI Latest Version

Self-hosted, actor-aware API usage analytics for Laravel.

Track who uses your API, which endpoints they use, how much they use them, and how they perform — without an external observability platform.

What problem does this solve?

Most Laravel applications can tell you that their API is busy. Far fewer can answer "how much did this customer use us last month", "which endpoint is getting slower", or "which API key is generating all the 422s" — the questions that show up in support threads, capacity planning and invoicing.

This package answers them from your own database. Every request is buffered in Redis after the response has been sent, flushed to SQL in batches by a scheduled command, and rolled up into per-actor, per-endpoint daily and monthly summaries you query through a small PHP API.

When should I use this?

You need Reach for
Distributed tracing across services OpenTelemetry
Debugging individual requests in development Telescope, a request logger
Error tracking and alerting Sentry, Bugsnag
API usage analytics per customer, tenant or API key This package

These are different jobs, not competitors. This package deliberately does not ship a dashboard, enforce quotas, trace across services, or record request bodies. See Out of scope.

Requirements

Supported
PHP 8.2, 8.3, 8.4, 8.5
Laravel 12.x, 13.x
Redis client ext-redis (recommended) or predis/predis
Database MySQL, MariaDB, PostgreSQL, SQLite, SQL Server

Quick Start

1. Install

2. Run migrations

The package ships its migrations and loads them automatically — there is nothing to publish. Two tables are created:

3. Make sure Redis is configured

Requests are appended to a Redis list before being flushed to SQL.

If the configured connection does not exist, the middleware records nothing rather than throwing — usage tracking never takes an application down.

4. Run the scheduler

The commands are registered on the scheduler automatically, but they only run if your scheduler runs.

5. Check that it is working

Architecture

The important property is where the line falls: nothing between the request arriving and the response leaving touches SQL. Actor resolution, endpoint resolution, serialization and the two Redis round trips all happen in terminate(), after the client already has its response.

Failure is handled the same way at every step. A minute buffer is claimed with RENAMENX into a private processing key, tracked in a Redis set, and only deleted once the database write is confirmed — so a crashed or failed flush is retried on the next run instead of silently losing entries. See What can be lost.

Terminology

Actor

An entity usage is attributed to. The package never assumes it is a User:

user:42 · organization:12 · tenant:acme · api_key:abc123 · service_account:billing · guest

An actor has a type and an id, both stored as first-class columns, plus an actor_key (type:id) for convenient grouping. Ids are normalized to strings, so integer and UUID keys behave identically.

Credential

Which key the actor used — a personal access token, an API key, an OAuth client. This is a second dimension, orthogonal to the actor: it lets you keep "the Acme team" as the actor while still breaking usage down per token.

Endpoint

The logical Laravel endpoint, not the concrete URL. The endpoint key prefers GET:api.orders.show over GET:/api/orders/123, so resource ids never fragment your analytics. The raw path is still stored on each row for debugging.

Actor Tracking

Out of the box, usage is attributed to the authenticated user, and to guest when there is none. That is one class:

Because Sanctum lets any model be tokenable, this already gives you team- or organization-level attribution when your tokens belong to a Team.

Custom actor resolver

Anything else is a resolver of your own:

Resolvers are built through the container, so constructor injection works. Returning null means do not record this request at all — that is how track_guests drops anonymous traffic.

Per-credential attribution

credential_id is filled by a callback you register. The package never guesses:

The same closure can live in actor.credential_resolver in the config file instead, at the cost of making the config uncacheable. A resolver registered in code wins.

Endpoint Tracking

RouteEndpointResolver is the default and needs no configuration. For unusual routing setups, implement ResolvesUsageEndpoint and point endpoint.resolver at it. Unlike actor resolution it never returns null: a request that matched no route is still recorded, keyed by its path.

Querying

Three entry points, all sharing the same period selection and filters.

Periods

today() · yesterday() · thisWeek() · lastDays(int $days) · thisMonth() · lastMonth() · between(DateTimeInterface $from, DateTimeInterface $to)

All dates are UTC and both ends of a range are inclusive. Without a period, a query covers the current month.

Filters

forActor(UsageActor $actor) · forActorType(string $type) · forCredential(string|int $id) · forEndpoint(string $endpointKey)

Queries are immutable, so a partially built query is safe to reuse:

Usage totals

UsageSummary exposes totalRequests, informational, successfulRequests, redirects, clientErrors, serverErrors, totalDurationMs, averageDurationMs, minDurationMs, maxDurationMs, plus errorRate(), serverErrorRate(), hasUsage() and toArray(). An empty period returns zero counts and null durations — never a misleading zero-millisecond average.

Endpoint analytics

Each EndpointUsage carries endpointKey, method, routeName, routeUri and a full UsageSummary. Ordering ties break on the endpoint key, so results are stable between runs.

Actor analytics

Each ActorUsage carries actorType, actorId, actorKey, a UsageSummary, and actor() to turn the result back into a UsageActor you can feed into another query. Different actor types never collide: user:42 and organization:42 are separate rows.

Query freshness

The query API reads daily summaries, not raw rows. Summaries are small, outlive raw retention, and already carry duration totals. The cost is freshness: numbers are as current as the last consolidation run.

By default the package re-consolidates the current day every hour, so today() is at most an hour behind. Consolidation is idempotent — a rerun recomputes the day rather than double-counting it — so you can run it as often as you like:

Turn the hourly run off with API_USAGE_SCHEDULE_CONSOLIDATE_TODAY=false on very high-volume installations, and accept that today's usage appears after the nightly run.

Raw access

Both Eloquent models are public API, and are the right tool for questions the query services do not cover:

Treat them as the advanced API. The query services are the stable surface; the schema may change in a future major version.

What Runs Automatically

Command Frequency Purpose
api-usage:flush --max-minutes=5 every minute Redis buffer → api_usage_requests
api-usage:consolidate-daily --today hourly Keeps today's summaries fresh
api-usage:consolidate-daily daily at 02:00 Yesterday's raw rows → daily summaries
api-usage:consolidate-monthly monthly on day 1 at 03:00 Daily → monthly summaries
api-usage:prune daily at 03:10 Applies the retention windows

Plus api-usage:status, which is never scheduled: it reads the enabled state, Redis and database connectivity, buffer depth, sampling rate and retention windows, and mutates nothing.

RecordApiUsage is appended to the api middleware group. If that group does not exist, registration is skipped silently.

Configuration

Defaults are usable as-is. Publish the config only if you need to change them:

Key Env Default Description
enabled API_USAGE_ENABLED true Master on/off switch
actor.resolver AuthenticatedUserActorResolver Class deciding who a request belongs to
actor.track_guests API_USAGE_TRACK_GUESTS true Record unauthenticated traffic
actor.credential_resolver null Callback resolving the credential used
endpoint.resolver RouteEndpointResolver Class deciding the endpoint identity
buffer.connection API_USAGE_REDIS_CONNECTION default Redis connection name
buffer.key_prefix API_USAGE_REDIS_KEY_PREFIX api_usage: Redis key prefix
buffer.ttl_seconds API_USAGE_REDIS_TTL_SECONDS 7200 Buffer key TTL (min 60)
buffer.flush_batch_size API_USAGE_FLUSH_BATCH_SIZE 1000 Rows per insert statement
database.connection API_USAGE_DB_CONNECTION null Dedicated connection, or the app default
database.tables.requests API_USAGE_TABLE_REQUESTS api_usage_requests Raw table name
database.tables.summaries API_USAGE_TABLE_SUMMARIES api_usage_summaries Aggregate table name
database.consolidation_chunk_size API_USAGE_CONSOLIDATION_CHUNK_SIZE 2000 Read chunk size during rollup
sampling.rate API_USAGE_SAMPLING_RATE 1.0 Fraction of requests recorded
except ['up', 'health'] Paths never recorded (supports *)
privacy.hash_ips API_USAGE_HASH_IPS true Store a salted hash, or nothing at all
privacy.ip_hash_salt API_USAGE_IP_HASH_SALT null Defaults to app.key
privacy.record_user_agent API_USAGE_RECORD_USER_AGENT true Store the user agent string
privacy.request_id_headers X-Request-Id, X-Correlation-Id Correlation headers, in priority order
retention.raw_days API_USAGE_RETENTION_RAW_DAYS 30 Raw row retention
retention.daily_days API_USAGE_RETENTION_DAILY_DAYS 730 Daily summary retention (0 = forever)
retention.monthly_months API_USAGE_RETENTION_MONTHLY_MONTHS 0 Monthly summary retention (0 = forever)
middleware.auto_register API_USAGE_AUTO_MIDDLEWARE true Auto-append the middleware
middleware.group API_USAGE_MIDDLEWARE_GROUP api Group to append the middleware to
schedule.enabled API_USAGE_SCHEDULE_ENABLED true Auto-register scheduled commands
schedule.flush_minutes API_USAGE_SCHEDULE_FLUSH_MINUTES 5 --max-minutes used by flush
schedule.consolidate_today API_USAGE_SCHEDULE_CONSOLIDATE_TODAY true Hourly refresh of today
schedule.daily_at API_USAGE_SCHEDULE_DAILY_AT 02:00 Daily consolidation time
schedule.monthly_at API_USAGE_SCHEDULE_MONTHLY_AT 03:00 Monthly consolidation time
schedule.prune_at API_USAGE_SCHEDULE_PRUNE_AT 03:10 Prune time

Excluding noisy endpoints

Sampling

Counts are never scaled back up. At a rate below 1.0 the package reports what it observed, not an estimate of the true total, because a number labelled "total requests" that is silently extrapolated is worse than an honest sample. Keep the rate at 1.0 if the counts must be exact — for billing, say.

Retention

Data Default Why
Raw rows 30 days Detailed, grows fastest, only needed for recent debugging
Daily summaries 730 days Small, and what the query API reads
Monthly summaries forever Tiny, and the basis for year-over-year comparisons

api-usage:prune applies all three, deletes in chunks so a large backlog never holds one long transaction open, and is safe to run repeatedly. Set a summary retention to 0 to keep it indefinitely.

Note that consolidation recomputes a day from raw rows, so re-running consolidate-daily for a date whose raw rows have been pruned will zero that day's summaries. Backfill before you prune, not after.

Privacy

What is stored:

What is never stored:

Two things to be aware of:

Reliability

What can be lost

What can be double-counted

Delivery into api_usage_requests is at-least-once, not exactly-once. A flush inserts the batch, then deletes the buffer. If the process is killed between those two steps, the next run replays the batch and those rows are inserted twice — inflating that day's raw rows and, once consolidated, its summaries.

The window is milliseconds wide and only opens on a hard kill, not on an ordinary error. It is called out here rather than papered over: if your counts feed invoicing, reconcile against a source that is exactly-once.

What is guaranteed

Manual Wiring

Data Model

All timestamps are stored in UTC, independent of app.timezone.

api_usage_requests

Column Notes
requested_at UTC
actor_type, actor_id, actor_key Ids are strings; actor_key is type:id or guest
credential_id Nullable; whatever the credential resolver returned
bucket_key actor_key, plus \|cred:{id} when a credential is known
method, route_name, route_uri, path path truncated to 1024 chars
endpoint_key METHOD:route_name, METHOD:route_uri or METHOD:/path
status_code, duration_ms
ip_hash Salted SHA-256, or null
user_agent Truncated to 512 chars, or null
request_id First matching correlation header, 64 chars

api_usage_summaries

One row per (period_type, period_start, bucket_key, endpoint_key) — the full aggregation identity, and the unique index consolidation upserts on.

period_type is day or month. Alongside the counters (total_requests, responses_1xxresponses_5xx) each row carries total_duration_ms, min_duration_ms and max_duration_ms.

bucket_key rather than credential_id carries the uniqueness on purpose: a nullable column in a unique index would defeat the upsert, because SQL treats every NULL as distinct and traffic without a credential would accumulate duplicate rows on every rerun. actor_type, actor_id and credential_id are still separate indexed columns, so you filter on those, never by parsing a key.

What This Package Deliberately Does Not Do

No dashboard. No Filament dependency. No billing or quota enforcement. No rate limiting. No request/response body logging. No distributed tracing or OpenTelemetry exporter. No query profiling or N+1 detection. No p95/p99 latency — that needs a histogram design this package does not have, and a percentile computed from daily min/max would be a lie.

Its whole job is: measure API usage; do not become an observability platform.

Testing

The suite runs against SQLite in memory with an in-memory Redis double, so no services are required.

Contributing

See SECURITY.md.

License

MIT — see LICENSE


All versions of laravel-api-usage with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
illuminate/console Version ^12.0|^13.0
illuminate/contracts Version ^12.0|^13.0
illuminate/database Version ^12.0|^13.0
illuminate/http Version ^12.0|^13.0
illuminate/redis Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
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 systemverk/laravel-api-usage contains the following files

Loading the files please wait ...