slimad / colnect-api
A lightweight Saloon SDK for the Colnect API
Requires
- php: ^8.2
- saloonphp/saloon: ^3.0
Requires (Dev)
- ext-pcov: *
- infection/infection: ^0.29
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-01 11:31:09 UTC
README
A lightweight, fully typed PHP SDK for the Colnect REST API, built on top of Saloon v3. It ships with first-class support for HMAC request signing, request mocking, PHPStan level 9 static analysis and mutation testing via Infection.
Table of contents
- Features
- Requirements
- Installation
- Quick start
- Authentication
- Available requests
- Working with categories
- Filtering lists
- Items and pictures
- Marketplace
- Image search
- Error handling
- Mocking & testing your integration
- Development
- Documentation
- Versioning
- Contributing
- Security
- License
- Attribution required by Colnect
- Credits
Features
- Full CAPI coverage - general, collector, category, search, image search and marketplace actions.
- Saloon v3 connector with a clean, request-per-endpoint architecture.
- Built-in HMAC-SHA256 signing of every outgoing request (
Capi-Timestamp+Capi-Hashheaders). - HTTP/2 by default - CAPI rejects HTTP/1.x with
426 Upgrade Required, so the connector asks Guzzle for HTTP/2. - Locale aware - the connector automatically routes requests through the localized Colnect endpoint (
/en,/pl,/fr, ...). - Composable filters - the documented common list filters (and the undocumented ones) through an immutable
Filtersobject. - Picture URL builder - turns picture IDs into thumbnail or full size image URLs, including the
urlize()slug rules. - Strongly typed with
declare(strict_types=1)everywhere and an enum-driven catalog of collectable categories (CategoryType). - Fails fast - malformed IDs, categories, page numbers or user agents throw before anything reaches the wire.
- Mock-friendly - integrates seamlessly with Saloon's
MockClient/MockResponsefor unit testing your own application. - Quality-first:
- PHPUnit 10
- PHPStan level 9
- Infection MSI (
@defaultmutators) withpcovcoverage
- PSR-4 autoloaded library with zero runtime dependencies beyond Saloon.
Requirements
| Component | Version |
|---|---|
| PHP | ^8.2 (tested on 8.2, 8.3 and 8.4) |
| saloonphp/saloon | ^3.0 |
| ext-pcov | dev only (for Infection coverage) |
CAPI is served exclusively over HTTP/2, so the cURL build used by Guzzle has to support it (curl --version must list HTTP2).
Installation
Install the package via Composer:
composer require slimad/colnect-api
The package is distributed under the
slimad/colnect-apiname. The autoload root isSlimad\ColnectApi\and maps tosrc/.
Quick start
<?php require __DIR__ . '/vendor/autoload.php'; use Slimad\ColnectApi\ColnectConnector; use Slimad\ColnectApi\Enums\CategoryType; use Slimad\ColnectApi\Requests\Category\GetCountriesRequest; use Slimad\ColnectApi\Requests\Category\GetItemsListRequest; use Slimad\ColnectApi\Requests\General\GetCategoriesRequest; use Slimad\ColnectApi\Requests\Search\GlobalSearchRequest; use Slimad\ColnectApi\Support\Filters; $connector = new ColnectConnector( appId: getenv('COLNECT_APP_ID'), appSecret: getenv('COLNECT_APP_SECRET'), language: 'en', userAgent: 'MyCoolApp/1.2.3.4', ); // 1. List every supported collectable category $categories = $connector->send(new GetCategoriesRequest())->json(); // 2. List the countries that issued a given collectable category $countries = $connector ->send(new GetCountriesRequest(CategoryType::Stamps)) ->json(); // 3. List the items of a country, newest first $items = $connector ->send(new GetItemsListRequest( CategoryType::Stamps, Filters::make()->producer(2610)->year(1980), )) ->json(); // 4. Search across a collectable type $results = $connector ->send(new GlobalSearchRequest(CategoryType::Stamps, 'olympic games', page: 2)) ->json();
Authentication
Colnect uses an appId + appSecret pair that must be requested from Colnect. Every request is signed with an HMAC-SHA256 hash:
Capi-Timestamp: <unix-timestamp>
Capi-Hash: hash_hmac('sha256', path + '>|<' + timestamp, appSecret)
The signing logic lives in ColnectConnector::boot() and is applied automatically to every request - you never have to touch it yourself.
$connector = new ColnectConnector( appId: 'your-app-id', appSecret: 'your-app-secret', language: 'pl', // resolves to https://api.colnect.net/pl/api/{appId} userAgent: 'MyCoolApp/1.2.3.4', // must clearly identify your app, 16+ characters );
Never commit your
appSecretto version control. Load it from environment variables, a secrets manager, or your framework's configuration layer. If a secret leaks, revoke it and generate a new one - Colnect accepts both secrets for a short grace period while you rotate.
Available requests
General actions
| Class | Endpoint | Notes |
|---|---|---|
General\GetCategoriesRequest |
GET /categories |
Category names currently available on Colnect. |
General\GetLanguagesRequest |
GET /languages |
short_iso => language_name. |
General\GetCountryFlagsRequest |
GET /country_flag/ids/{ids} |
country_id => flag_picture_id, see PictureUrl::flag(). |
General\GetRequestCountRequest |
GET /request_count[/days/{days}] |
Your own daily CAPI usage, up to 200 days. |
General\TranslatePhrasesRequest |
POST /translate_phrases |
Phrases are sent as a JSON array in the body. |
Collector actions
| Class | Endpoint |
|---|---|
Collector\GetRatingsCountRequest |
GET /ratings_count/collector/{collector} |
Collector\GetRatingsReceivedRequest |
GET /ratings_received/collector/{collector} |
Collector\GetRatingsGivenRequest |
GET /ratings_given/collector/{collector} |
Privacy settings may make Colnect answer private instead of the data.
Category actions
| Class | Endpoint | Mandatory arguments |
|---|---|---|
Category\GetCountriesRequest |
GET /countries/cat/{category} |
- |
Category\GetProducersRequest |
GET /producers/cat/{category}/country/{country} |
country |
Category\GetSeriesRequest |
GET /series/cat/{category}/producer/{producer} |
producer |
Category\GetYearsRequest |
GET /years/cat/{category}/producer/{producer} |
producer |
Category\GetThemesRequest |
GET /themes/cat/{category} |
- |
Category\GetCatalogsRequest |
GET /catalogs/cat/{category} |
- |
Category\GetCurrenciesRequest |
GET /currencies/cat/{category} |
- |
Category\GetFaceValuesRequest |
GET /face_values/cat/{category} |
- |
Category\GetSystemsRequest |
GET /systems/cat/{category} |
- |
Category\GetItemsListRequest |
GET /list/cat/{category}/{filters} |
at least one filter |
Category\GetItemIdsRequest |
GET /list_id/cat/{category}/{filters} |
at least one filter |
Category\GetItemRequest |
GET /item/cat/{category}/id/{ids} |
1 to 50 item IDs |
Category\GetFieldTitlesRequest |
GET /field_titles/cat/{category} |
- |
Category\GetProducerIsCountryRequest |
GET /producer_is_country/cat/{category} |
- |
Category\GetCategoryActionsRequest |
GET /category_actions/cat/{category} |
- |
Category\GetCategorySortOptionsRequest |
GET /category_sort_options/cat/{category} |
- |
Not every action exists in every category - ask GetCategoryActionsRequest when in doubt.
Search
| Class | Endpoint |
|---|---|
Search\GlobalSearchRequest |
GET /search[/collectibles/{type}]/q/{query}[/page/{page}] |
Search\ImageSearchRequest |
POST /image_search[/cat/{category}]/{filters} |
Marketplace
| Class | Endpoint |
|---|---|
Marketplace\GetMarketPricesRequest |
GET /market_prices/cat/{category}/ids/{ids}[/days/{days}] |
Marketplace\GetItemSalesRequest |
GET /item_sales/cat/{category}/item_id/{ids}[/...] |
All request classes follow the same pattern:
$request = new GetCountriesRequest(CategoryType::Coins); $response = $connector->send($request); $body = $response->body(); // raw string $json = $response->json(); // array $status = $response->status(); // int
Working with categories
The CategoryType enum provides a typed list of the collectable categories supported by Colnect, using the very module names Colnect expects in the cat filter:
use Slimad\ColnectApi\Enums\CategoryType; CategoryType::Coins; // 'coins' CategoryType::Stamps; // 'stamps' CategoryType::Banknotes; // 'banknotes' CategoryType::TradingCardGames; // 'trading_card_games' // ...46 categories in total
Each category-aware request accepts either the enum or a raw string, so you can interoperate with values coming from forms, configs, or HTTP input:
new GetCountriesRequest(CategoryType::Stamps); // typed new GetCountriesRequest('stamps'); // string, still valid
Colnect names the producer differently per category ("company" for phonecards, "brand" for teabags, the country itself for stamps). The enum knows the mapping:
CategoryType::Phonecards->producerFilter(); // 'company' CategoryType::Stamps->producerFilter(); // 'country' CategoryType::Stamps->producerIsCountry(); // true
Categories are edited continuously on Colnect, so treat the enum as a convenience and GetCategoriesRequest as the source of truth.
Filtering lists
Filters is an immutable collection rendering the documented common list filters as path segments, in the order you add them:
use Slimad\ColnectApi\Support\Filters; $filters = Filters::make() ->country(5) ->year(1980) ->theme(7) ->sort('face_value') // see GetCategorySortOptionsRequest ->page(2); $connector->send(new GetItemsListRequest(CategoryType::Stamps, $filters)); // GET /list/cat/stamps/country/5/year/1980/theme/7/sort/face_value/page/2
Named helpers exist for country, producer, series, year, theme, catalog, currency, face_value, system, sort and page. Colnect exposes more filters while you navigate the site; reach them with with():
Filters::make()->with('in_stock', 1);
Filter IDs are Colnect-internal and change over time - the API documentation asks you not to cache them for more than 24 hours.
Items and pictures
GetItemRequest returns the fields listed by GetFieldTitlesRequest, in the same order. The fields differ per category and may change, so read them at runtime instead of hard-coding offsets:
$fields = $connector->send(new GetFieldTitlesRequest(CategoryType::Coins))->json(); $item = $connector->send(new GetItemRequest(CategoryType::Coins, 35693))->json(); // Up to 50 items per call - the response is then an array of arrays $items = $connector->send(new GetItemRequest(CategoryType::Coins, [35693, 12594]))->json(); // Resolve referenced records (themes, catalog codes, ...) in the same call $detailed = $connector->send(new GetItemRequest(CategoryType::Coins, 35693, includeDetails: true))->json();
Picture IDs become URLs through PictureUrl:
use Slimad\ColnectApi\Enums\PictureSize; use Slimad\ColnectApi\Support\PictureUrl; PictureUrl::item(142891, 'Pasteur-Louis'); // https://i.colnect.net/images/t/142/891/Pasteur-Louis.jpg PictureUrl::item(1234567, 'Some Name', PictureSize::Full); // https://i.colnect.net/images/f/1234/567/Some_Name.jpg PictureUrl::flag(469); // https://i.colnect.net/flag/32/469-country.png
The item name is slugged with Str::urlize(), the port of the function published in the API specification.
Marketplace
use Slimad\ColnectApi\Enums\ItemsInSale; use Slimad\ColnectApi\Requests\Marketplace\GetItemSalesRequest; use Slimad\ColnectApi\Requests\Marketplace\GetMarketPricesRequest; // Sale prices of the last 30 days, per condition $prices = $connector ->send(new GetMarketPricesRequest(CategoryType::Coins, [35693, 12594], days: 30)) ->json(); // Up to 20 single-item sales currently on the market $sales = $connector ->send(new GetItemSalesRequest(CategoryType::Coins, 35693, ItemsInSale::Single, maxSales: 20)) ->json();
Image search
Image search is disabled by default and billed per call, so it has to be enabled for your application first. Because image recognition is imperfect, narrowing the search down with a category and filters is recommended.
use Slimad\ColnectApi\Requests\Search\ImageSearchRequest; use Slimad\ColnectApi\Support\ImageSearchTarget; $target = ImageSearchTarget::fromUrl('https://example.com/coin-front.jpg') ->withBackUrl('https://example.com/coin-back.jpg'); // coins only $matches = $connector ->send(new ImageSearchRequest($target, CategoryType::Coins, Filters::make()->country(5))) ->json();
A target can also be built from base64 data (ImageSearchTarget::fromBase64()) or from a local file (ImageSearchTarget::fromFile()), each with a withBack*() counterpart. The response carries distances and best_matches, where c_id is the item ID and p_id the picture ID.
Error handling
Every malformed argument throws Slimad\ColnectApi\Exceptions\InvalidArgumentException, which extends the SPL \InvalidArgumentException:
use Slimad\ColnectApi\Exceptions\InvalidArgumentException; try { new GetItemRequest(CategoryType::Coins, range(1, 51)); // 50 IDs max } catch (InvalidArgumentException $e) { // 'Item IDs must not contain more than 50 IDs.' }
HTTP errors are handled by Saloon as usual - use $response->failed(), $response->status() or $connector->send() with ->throw().
Mocking & testing your integration
Because the SDK is built on Saloon, you can mock every response without hitting the network - ideal for fast, deterministic unit tests:
use Saloon\Http\Faking\MockClient; use Saloon\Http\Faking\MockResponse; use Slimad\ColnectApi\ColnectConnector; use Slimad\ColnectApi\Requests\General\GetLanguagesRequest; $mockClient = new MockClient([ GetLanguagesRequest::class => MockResponse::make([ 'en' => 'English', 'pl' => 'Polski', ], status: 200), ]); $connector = (new ColnectConnector('app_id', 'app_secret')) ->withMockClient($mockClient); $languages = $connector->send(new GetLanguagesRequest())->json();
See tests/ColnectConnectorTest.php for end-to-end examples of asserting headers, payloads and the HMAC signature.
Development
Clone the repository and install dev dependencies:
git clone https://github.com/maciej-kosiedowski/Colnect-api.git
cd Colnect-api
composer install
Running the test suite
composer test # or vendor/bin/phpunit
The test suite runs against:
tests/ColnectConnectorTest.php– connector + auth flowtests/Requests/**/*Test.php– one test per request class
Static analysis
PHPStan is configured at level 9 for both src/ and tests/:
composer analyse
# or
vendor/bin/phpstan analyse src tests --level 9
Mutation testing
Infection is configured with the @default mutator set and source-only coverage:
composer mutate
# or
vendor/bin/infection --show-mutations --coverage=build/coverage
The pcov extension is required for coverage; it is already declared as a dev dependency in composer.json.
Documentation
- API reference – the source of truth for available endpoints is the official Colnect API documentation. This SDK is a thin, typed wrapper over those endpoints. Not every action exists in every category, and Colnect takes requests for missing actions on the CAPI forum.
- Saloon documentation – for advanced features such as request concurrency, retries, plugins, response DTOs, pagination, OAuth flows and caching, see the Saloon v3 docs.
- Source code – every public class is annotated with PHPDoc. The fastest way to discover the surface area of this library is to browse
src/. - Examples – tests under
tests/double as runnable usage examples for every request class.
Versioning
This project follows Semantic Versioning 2.0.0:
- MAJOR – incompatible API changes.
- MINOR – backwards-compatible feature additions (new requests, new enum cases, …).
- PATCH – backwards-compatible bug fixes and internal refactors.
Pin a major version in your composer.json to avoid unexpected breaking changes:
{
"require": {
"slimad/colnect-api": "^1.0"
}
}
Contributing
Contributions are welcome and appreciated! To contribute:
- Fork the repository and create a feature branch off
main. - Add code and tests – PRs without tests will not be merged.
- Make sure the full quality gate passes locally:
composer test composer analyse composer mutate - Follow the existing coding style –
declare(strict_types=1), typed properties, PSR-12 formatting. - Open a pull request describing what changed and why.
Bug reports and feature requests can be filed via GitHub Issues. Please include a minimal reproducible example whenever possible.
Security
Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.
If you discover a security vulnerability in this package, report it privately so it can be addressed before public disclosure:
- Preferred channel: open a GitHub private security advisory for this repository.
- Alternative: email the maintainer directly at the address listed on the maintainer's GitHub profile. Use a clear subject such as
[SECURITY] Colnect-api – <short summary>.
When reporting, please include:
- A description of the vulnerability and the impact you believe it may have.
- Step-by-step instructions to reproduce the issue (proof of concept code is ideal).
- The affected version(s) of the package.
- Any suggested remediation, if known.
You can expect:
- An initial acknowledgement within 72 hours.
- A status update at least every 7 days until the issue is resolved.
- Public disclosure (CVE if applicable) coordinated with you after a fix is released.
Please act in good faith and avoid:
- Exploiting the vulnerability beyond what is necessary to demonstrate it.
- Disclosing the issue publicly before a fix is available.
- Accessing data that is not yours.
License
This package is open-sourced software licensed under the MIT License.
MIT License
Copyright (c) 2026 Maciej Kosiedowski
See the LICENSE file for the full text. In short, you are free to use, copy, modify, merge, publish, distribute, sublicense and/or sell copies of the software, provided the original copyright notice and the license text are included in all copies or substantial portions of the software. The software is provided "as is", without warranty of any kind.
Credits
- Author / Maintainer: Maciej Kosiedowski
If this package is useful to you, please consider starring the repository on GitHub ⭐.