Readonly typed models, named arguments, zero Composer dependencies
(uses ext-curl). PHP ≥ 8.1.
composer require inbio/inbioShorten a URL (no account)
use Inbio\Inbio;
$link = Inbio::shorten('https://example.com/very/long/url');
echo $link->short_url; // https://in.bio/a8K2pAuthenticated client
Pass a token or set INBIO_API_TOKEN in the environment:
use Inbio\Client;
$client = new Client(); // reads INBIO_API_TOKEN
$created = $client->links->create(
'https://example.com/sale',
slug: 'spring-sale',
tags: ['marketing'],
utm: ['source' => 'newsletter', 'medium' => 'email'],
);Everything else
// List with filters, or iterate every page automatically
$page = $client->links->list(status: 'active', perPage: 50);
foreach ($client->links->iterate(tag: 'marketing') as $link) {
echo $link->slug, ' ', $link->total_clicks, PHP_EOL;
}
// Update, toggle, delete
$client->links->update(42, title: 'Renamed');
$client->links->disable(42);
$client->links->enable(42);
$client->links->delete(42);
// QR code bytes → file
file_put_contents('qr.png', $client->links->qr(42, format: 'png', size: 1024));
// Analytics, folders, tags, usage
$stats = $client->links->analytics(42, from: '2026-06-01');
$folders = $client->folders->list();
$tags = $client->tags->list();
$usage = $client->account->usage();Errors
use Inbio\Exception\RateLimitException;
use Inbio\Exception\ValidationException;
try {
$client->links->create($url);
} catch (ValidationException $e) {
print_r($e->errors); // per-field messages
} catch (RateLimitException $e) {
echo $e->retryAfter; // seconds
}AuthenticationException (401), AccessException (403, plan or scope),
NotFoundException, ValidationException, EntitlementException,
StateConflictException (409), RateLimitException (429), ServerException
— all extend Inbio\Exception\InbioException. GETs and Retry-After 429s
are retried automatically with backoff.
Webhooks
$event = $client->webhooks->verify(
file_get_contents('php://input'), // raw body
$_SERVER['HTTP_X_INBIO_SIGNATURE'],
$secret,
);
if ($event->event === 'link.clicked') {
// ...
}Signatures are checked with hash_equals and a 5-minute replay-protection
window. See Webhooks for the payload format.