dts/ebay-sdk-finding
dts/ebay-sdk-finding is a PHP SDK for eBay's Finding API, enabling listing search, item lookup, and catalog queries via a clean client. Works with Laravel or standalone PHP and supports configurable credentials and request handling.
This SDK is a thin, generated PHP client for eBay's Finding API (keyword/product search). It is deprecated — the repo says to use dts/ebay-sdk-php instead — so treat any adoption as a short-term bridge, not a foundation.
Minimal setup:
composer require dts/ebay-sdk-finding
Check the resolved PHP constraint first (composer show dts/ebay-sdk-finding). The package predates modern PHP and may not install cleanly on PHP 8.2+, in which case pin it only if you must, or fork it.
Credentials: the Finding API authenticates with an eBay App ID (no OAuth token or secret), obtained from the eBay Developers Program. Put it in config/services.php:
// config/services.php
'ebay' => [
'app_id' => env('EBAY_APP_ID'),
'global_id'=> env('EBAY_GLOBAL_ID', 'EBAY-US'),
],
First use case — a keyword search:
use DTS\eBaySDK\Finding\Services\FindingService;
use DTS\eBaySDK\Finding\Types\FindItemsByKeywordsRequest;
$service = new FindingService([
'appId' => config('services.ebay.app_id'),
'globalId' => config('services.ebay.global_id'),
]);
$request = new FindItemsByKeywordsRequest();
$request->keywords = 'harry potter first edition';
$response = $service->findItemsByKeywords($request);
if ($response->ack !== 'Success') {
// inspect $response->errorMessage->error
}
foreach ($response->searchResult->item ?? [] as $item) {
$item->itemId;
$item->title;
$item->sellingStatus->currentPrice->value;
$item->sellingStatus->currentPrice->currencyId;
}
Where to look first:
vendor/dts/ebay-sdk-finding/src/Services/FindingService.php — the list of available operations (findItemsByKeywords, findItemsAdvanced, findItemsByCategory, findCompletedItems, getSearchKeywordsRecommendation, findItemsByProduct, etc.).src/Types/ — every request/response class. The request class name tells you the operation; the response class tells you every field you can safely read.Wrap it, don't inject it. The service object holds config and an HTTP client; constructing it per-request is wasteful, and depending on it directly couples your app to a dead package. Bind a single wrapper in the container:
// app/Services/Ebay/KeywordSearch.php
class KeywordSearch
{
public function __construct(private FindingService $service) {}
public function search(string $keywords, int $page = 1, int $perPage = 100): array
{
$request = new FindItemsByKeywordsRequest();
$request->keywords = $keywords;
$request->paginationInput = new PaginationInput();
$request->paginationInput->entriesPerPage = $perPage;
$request->paginationInput->pageNumber = $page;
$response = $this->service->findItemsByKeywords($request);
return $this->normalise($response);
}
}
Register FindingService as a singleton in a service provider, reading config('services.ebay'). Keep the SDK's Types classes out of controllers and jobs — return arrays or your own DTOs (['id' => ..., 'title' => ..., 'price' => ..., 'currency' => ...]). This is the single biggest lever for a later migration to the Browse API: only the wrapper changes.
Filters via ItemFilter collections:
$request->itemFilter[] = new ItemFilter();
$request->itemFilter[0]->name = 'Condition';
$request->itemFilter[0]->value = ['New'];
Pagination. Finding caps results at 100 entries per page and (practically) 100 pages / 10,000 items. Loop while pageNumber < $response->paginationOutput->totalPages, and enforce your own hard stop. For "show me the top N" UI, one request with entriesPerPage = 100 is almost always enough — don't build a crawler.
Caching is mandatory, not optional. Call quotas are per-app and modest. Cache the normalised result keyed by sha1(globalId|keywords|filters|page) in Redis with a short TTL (5–30 min). This also smooths over the API's occasional latency and failures.
Queue the slow stuff. Any bulk lookup (price comparison across many keywords, nightly watchlists) belongs in a queued job with $tries, backoff, and rate limiting (Redis::throttle(...)->allow(...)->every(...)). Never fan out parallel requests from a web request — you'll hit throttling and a 10s HTTP timeout.
Multi-site. globalId is per-service-config in the SDK, so one singleton only serves one marketplace. If you need EBAY-US and EBAY-DE, register keyed instances (ebay.finding.us, ebay.finding.de) rather than mutating config at runtime.
Testing. Never let tests hit the network. Type-hint your KeywordSearch wrapper and swap it for a fake returning fixture arrays. Keep a couple of recorded FindItemsByKeywordsResponse payloads for a single "contract" test of the parsing layer, so you notice when someone changes the field names.
Error semantics. $response->ack is one of Success, Warning, Failure, PartialFailure. Treat Warning as success but log $response->errorMessage->error; treat Failure as an exception. Wrap API calls in a small retry (2–3 attempts, jittered backoff) for transport-level errors, not for Failure acks.
dts/ebay-sdk-php (Browse API) — same author, same object model, actively maintained.composer require fights you, fork and relax the constraints rather than downgrading your app.$response->searchResult->item is null when there are zero results — not an empty array. Always ?? [].currentPrice->value is a string like "12.99"; currencyId is separately populated. Cast and store the currency alongside the amount, or you'll silently mix USD and GBP.$request->paginationInput->pageNumber = 2 on a fresh request will fail; construct the sub-object first. The same applies to itemFilter entries.entriesPerPage above 100 is rejected, and pageNumber beyond totalPages returns an error rather than an empty set — clamp before requesting.appId is effectively a public identifier but still a quota-bearing credential. Keep it in .env, never in a repo, and rotate if it leaks. There's no secret to pair with it, so quota abuse is the real risk.FindingService constructor options for a handler/HTTP-client override in your version; if absent, that's another reason to migrate.listingInfo->startTime, endTime) come back as UTC XML datetimes. Parse with Carbon::parse($s, 'UTC') and convert at the edges.How can I help you explore Laravel packages today?