Skip to content

Product API v3 ​

The Product API v3 endpoint (POST /torob_api/v3/products) is invoked by Torob to crawl the full catalog or refresh specific products.

Request Modes ​

Torob will send one of four mutually exclusive payloads:

1. By URLs ​

json
{
  "page_urls": ["https://yourshop.com/product/34/", "https://yourshop.com/product/35/"]
}

2. By Unique Identifiers ​

json
{
  "page_uniques": ["12412_1", "12412_2"]
}

3. Page-Based Pagination ​

json
{
  "page": 1,
  "sort": "date_added_desc"
}

Supported sorts: date_added_desc, date_updated_desc, or product_id_desc.

4. Cursor-Based Pagination ​

First request:

json
{
  "sort": "product_id_desc"
}

Subsequent request:

json
{
  "cursor": "12345",
  "sort": "product_id_desc"
}

Implementation Guide ​

To connect your catalog, create a service implementing TorobProductProviderContract:

php
namespace App\Services;

use Torob\Laravel\Contracts\TorobProductProviderContract;
use Torob\Laravel\Contracts\ProductPageResult;
use Torob\Laravel\Contracts\ProductCursorResult;
use Torob\Laravel\DTO\TorobProduct;
use App\Models\Product;

class TorobProductProvider implements TorobProductProviderContract
{
    public function getByUrls(array $urls): array
    {
        return Product::whereIn('url', $urls)
            ->get()
            ->map(fn ($p) => $this->map($p))
            ->all();
    }

    public function getByUniques(array $uniques): array
    {
        return Product::whereIn('id', $uniques)
            ->get()
            ->map(fn ($p) => $this->map($p))
            ->all();
    }

    public function getPaginated(int $page, string $sort): ProductPageResult
    {
        $query = Product::query();

        match ($sort) {
            'date_added_desc' => $query->orderBy('created_at', 'desc'),
            'date_updated_desc' => $query->orderBy('updated_at', 'desc'),
            'product_id_desc' => $query->orderBy('id', 'desc'),
        };

        $paginator = $query->paginate(100, ['*'], 'page', $page);

        return new ProductPageResult(
            products: $paginator->getCollection()->map(fn ($p) => $this->map($p))->all(),
            total: $paginator->total(),
            currentPage: $paginator->currentPage(),
            maxPages: $paginator->lastPage()
        );
    }

    public function getCursor(?string $cursor, string $sort): ProductCursorResult
    {
        $query = Product::orderBy('id', 'desc')->limit(100);

        if ($cursor !== null) {
            $query->where('id', '<', $cursor);
        }

        $items = $query->get();
        $nextCursor = $items->count() === 100 ? (string) $items->last()->id : null;

        return new ProductCursorResult(
            products: $items->map(fn ($p) => $this->map($p))->all(),
            nextCursor: $nextCursor
        );
    }

    protected function map(Product $p): TorobProduct
    {
        return new TorobProduct(
            pageUnique: (string) $p->id,
            pageUrl: url("/product/{$p->slug}"),
            title: $p->name,
            currentPrice: $p->is_available ? $p->price : 0,
            availability: (bool) $p->is_available,
            imageLinks: [$p->main_image_url, ...$p->gallery_urls],
            dateAdded: $p->created_at->toIso8601String(),
            spec: $p->specs ?? [],
            dateUpdated: $p->updated_at?->toIso8601String()
        );
    }
}

Bind in AppServiceProvider:

php
$this->app->bind(
    \Torob\Laravel\Contracts\TorobProductProviderContract::class,
    \App\Services\TorobProductProvider::class
);

Response Schema ​

The controller automatically formats the payload to match Torob API v3:

json
{
  "api_version": "torob_api_v3",
  "current_page": 1,
  "total": 150,
  "max_pages": 2,
  "next_cursor": null,
  "products": [
    {
      "page_unique": "12412_1",
      "page_url": "https://domain.com/product/34/",
      "title": "Xiaomi Mi Note 10 Pro",
      "current_price": 5000000,
      "availability": true,
      "image_links": ["https://domain.com/images/main.jpg"],
      "spec": { "memory": "4GB" },
      "date_added": "2024-11-22T01:15:12+03:30"
    }
  ]
}

Released under the MIT License.