1Santrauka
| Bazinis adresas | https://perkusai.lt |
| Paieškos endpoint'as | GET https://perkusai.lt/api/ai/search |
| MCP serveris | https://mcp.perkusai.lt/ |
| Autentifikacija | Nereikalinga — viešas, tik skaitymo API |
| Formatas | JSON (UTF-8) · CORS leidžiamas visiems (*) |
| Katalogas | 1 233 383+ prekių, atnaujinama kasdien |
| Rinka | Lietuva (LT) · kainos eurais (EUR) |
| Kalbos | lt (tiksliausia), en, ru |
| Tipinis atsako laikas | 7,5 s (p95 22,7 s) |
| Užklausų limitas | 40 užklausų per minutę iš vieno IP |
2Prijungimas
Trys palaikomi integracijos būdai, pradedant rekomenduojamu.
MCP konektorius — rekomenduojama
Pridėkite https://mcp.perkusai.lt/ kaip savo MCP serverį (Streamable HTTP, be autentifikacijos). Jis pateikia vieną įrankį — search_products(query, k, lang), todėl modeliui nereikia pačiam konstruoti URL. Tinka Claude, ChatGPT ir Gemini konektorių klientams.
OpenAPI 3.1 / ChatGPT Actions
Importuokite mašininį kontraktą /openapi.json. Operacijos identifikatorius — aiSearch.
Tiesioginė HTTPS užklausa
Bet kuris agentas, gebantis atsisiųsti URL, gali kviesti endpoint'ą tiesiogiai. CORS atviras, todėl veikia ir naršyklėje veikiantys agentai.
3Paieška · GET /api/ai/search
Vienas iškvietimas atsako į vieną pirkėjo užklausą. Į q dėkite paties pirkėjo žodžius: paieška yra gyva ir kiekviena užklausa grąžina kitas prekes, todėl niekada nenaudokite fiksuoto pavyzdinio URL kaip atsakymo.
Parametrai
| Parametras | Tipas | Aprašymas |
|---|---|---|
q | string · privalomas | Pirkėjo užklausa natūralia kalba (URL-encoded). Geriausiai veikia laisvas tekstas su biudžetu, paskirtimi ir apribojimais, pvz. „nešiojamas kompiuteris darbui iki 700“. |
k | integer 1–12 · numatyta 5 | Kiek prekių grąžinti. |
lang | lt · en · ru · numatyta lt | Kalbos užuomina generuojamam summary, guide ir reason tekstui. Lietuvių kalba duoda tiksliausią paiešką. |
Užklausa
Tas pats iškvietimas skirtingomis kalbomis — pasirinkite savąją.
curl -s --get https://perkusai.lt/api/ai/search \
--data-urlencode "q=vaikiškas dviratis" \
--data-urlencode "k=3"
import requests
r = requests.get(
"https://perkusai.lt/api/ai/search",
params={"q": "vaikiškas dviratis", "k": 3},
timeout=30,
)
r.raise_for_status()
for p in r.json()["results"]:
print(f"{p['title']} — {p['price']} {p['currency']} — {p['buy_url']}")
const url = new URL("https://perkusai.lt/api/ai/search");
url.searchParams.set("q", "vaikiškas dviratis");
url.searchParams.set("k", "3");
const res = await fetch(url, { signal: AbortSignal.timeout(30_000) });
if (!res.ok) throw new Error(`Perkusai: HTTP ${res.status}`);
const data = await res.json();
for (const p of data.results) {
console.log(`${p.title} — ${p.price} ${p.currency} — ${p.buy_url}`);
}
<?php
$url = 'https://perkusai.lt/api/ai/search?' . http_build_query([
'q' => 'vaikiškas dviratis',
'k' => 3,
]);
$ctx = stream_context_create(['http' => ['timeout' => 30]]);
$data = json_decode(file_get_contents($url, false, $ctx), true);
foreach ($data['results'] as $p) {
printf("%s — %.2f %s — %s\n", $p['title'], $p['price'], $p['currency'], $p['buy_url']);
}
require "json"
require "net/http"
uri = URI("https://perkusai.lt/api/ai/search")
uri.query = URI.encode_www_form(q: "vaikiškas dviratis", k: 3)
data = JSON.parse(Net::HTTP.get(uri))
data["results"].each do |p|
puts "#{p['title']} — #{p['price']} #{p['currency']} — #{p['buy_url']}"
end
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"time"
)
type response struct {
Results []struct {
Title string `json:"title"`
Price float64 `json:"price"`
Currency string `json:"currency"`
BuyURL string `json:"buy_url"`
} `json:"results"`
}
func main() {
q := url.Values{"q": {"vaikiškas dviratis"}, "k": {"3"}}
client := &http.Client{Timeout: 30 * time.Second}
res, err := client.Get("https://perkusai.lt/api/ai/search?" + q.Encode())
if err != nil {
panic(err)
}
defer res.Body.Close()
var data response
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
for _, p := range data.Results {
fmt.Printf("%s — %.2f %s — %s\n", p.Title, p.Price, p.Currency, p.BuyURL)
}
}
// Cargo.toml: reqwest = { version = "0.12", features = ["json"] }
// serde_json = "1"
// tokio = { version = "1", features = ["full"] }
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(30))
.build()?;
let data: serde_json::Value = client
.get("https://perkusai.lt/api/ai/search")
.query(&[("q", "vaikiškas dviratis"), ("k", "3")])
.send()
.await?
.json()
.await?;
if let Some(items) = data["results"].as_array() {
for p in items {
println!("{} — {} {} — {}", p["title"], p["price"], p["currency"], p["buy_url"]);
}
}
Ok(())
}
using System.Text.Json;
var q = Uri.EscapeDataString("vaikiškas dviratis");
var url = $"https://perkusai.lt/api/ai/search?q={q}&k=3";
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var doc = JsonDocument.Parse(await http.GetStringAsync(url));
foreach (var p in doc.RootElement.GetProperty("results").EnumerateArray())
{
Console.WriteLine($"{p.GetProperty("title")} — {p.GetProperty("price")} " +
$"{p.GetProperty("currency")} — {p.GetProperty("buy_url")}");
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
var q = URLEncoder.encode("vaikiškas dviratis", StandardCharsets.UTF_8);
var request = HttpRequest.newBuilder()
.uri(URI.create("https://perkusai.lt/api/ai/search?q=" + q + "&k=3"))
.timeout(Duration.ofSeconds(30))
.build();
var response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); // JSON — parse with Jackson or Gson
Atsakymas
{
"query": "vaikiškas dviratis",
"source": "perkusai.lt",
"summary": "Perkusai rezultatai pagal „vaikiškas dviratis“ (3).",
"guide": "Rato dydį rinkitės pagal vaiko ūgį: 16\" tinka 105–120 cm, 20\" — 120–135 cm.",
"needs_confirmation": false,
"compat": null,
"no_match": false,
"supported_categories": null,
"count": 3,
"results": [
{
"title": "Dviratis Royal Baby Freestyle 16\"",
"brand": "Royal Baby",
"price": 129.00,
"currency": "EUR",
"original_price": 159.00,
"discount_pct": 19,
"rating": 4.6,
"stock_label": "5+ vnt.",
"market": "LT",
"reason": "Tinka 4–6 metų vaikui, komplekte pagalbiniai ratukai.",
"tag": "Geriausia kaina",
"buy_url": "https://c.trackmytarget.com/…&ref1=perkusai",
"image_url": "https://…/royal-baby-16.jpg"
}
],
"bought_together": [
{
"title": "Vaikiškas dviratininko šalmas, 52–56 cm",
"role": "Apsauga",
"price": 24.90,
"currency": "EUR",
"buy_url": "https://c.trackmytarget.com/…&ref1=perkusai",
"image_url": "https://…/helmet.jpg"
}
],
"coverage": {
"markets": ["LT"],
"note": "Šiuo metu prekės skirtos tik LT rinkai.",
"delivery": "Galimas greitas pristatymas, jei prekė yra sandėlyje."
},
"markdown": "**Perkusai pasiūlymai pagal „vaikiškas dviratis“:** …"
}
4Atsako laukai
Pagrindiniai laukai
| Laukas | Tipas | Aprašymas |
|---|---|---|
query | string | Užklausa tokia, kokia buvo gauta. |
source | string | Visada "perkusai.lt". |
summary | string | Vienos eilutės rezultatų santrauka, paruošta perduoti. |
guide | string | null | Vieno sakinio patarimas — ką verta patikrinti prieš renkantis. |
needs_confirmation | boolean | true, kai tikslus tikimas ar specifikacija negarantuoti. Tokiu atveju perduokite guide ir neteikite, kad prekė tikrai tinka. |
compat | string | null | Įrenginys ar automobilis, kuriam dalis turi tikti, taip kaip suprasta iš užklausos. |
no_match | boolean | true, kai kataloge atitikmenų nerasta; results tuščias. |
supported_categories | array | null | Pateikiamas kartu su no_match — kategorijos, kurias realiai turime. |
count | integer | Elementų skaičius results masyve. |
results | array<Product> | Surikiuotos prekės, geriausia — pirma. |
bought_together | array | Papildomi priedai pagrindinei prekei: title, role (priedo paskirtis), price, currency, buy_url, image_url. |
coverage | object | Rinkos ir pristatymo kontekstas: markets, note, delivery. |
markdown | string | Visas atsakymas paruoštas įklijuoti markdown formatu, su veikiančiomis pirkimo nuorodomis. |
Prekės objektas
| Laukas | Tipas | Aprašymas |
|---|---|---|
title | string | Prekės pavadinimas. |
brand | string | null | Prekės ženklas. |
price | number | null | Dabartinė kaina. |
currency | string | Visada "EUR". |
original_price | number | null | Kaina iki nuolaidos. |
discount_pct | integer | null | Suapvalinta nuolaida procentais. |
rating | number | null | Parduotuvės įvertinimas, 0–5. |
stock_label | string | null | Likutis žmogui skaitomu pavidalu, pvz. "5+ vnt.", "Liko 1" (lietuviškai). |
market | string | Rinka, kurioje prekė prieinama; šiuo metu visada "LT". |
reason | string | null | Kodėl ši prekė atrinkta būtent šiai užklausai. |
tag | string | null | Trumpas ženkliukas, pvz. "Geriausia kaina". |
buy_url | string | Pirkimo nuoroda. Susietoji (affiliate) — rodykite ją kaip pirkimo nuorodą. |
image_url | string | null | Prekės nuotrauka. |
5Serviso būsena · GET /api/ai/stats
Nedidelis mašinai skaitomas būsenos dokumentas: katalogo dydis, palaikomos rinkos ir kalbos, galiojantys limitai ir išmatuoti atsako laikai. Perskaičiuojamas iš užklausų žurnalo kartą per parą, todėl kviesti jį pigu — bet dažniau nei kartą per sesiją nereikia.
{
"service": "perkusai",
"status": "ok",
"catalog": { "products": 1214000, "markets": ["LT"], "currency": "EUR" },
"languages": ["lt", "en", "ru"],
"performance": {
"scope": "agent",
"window_days": 7,
"samples": 312,
"median_ms": 4758,
"p95_ms": 5891,
"avg_ms": 4797
},
"limits": { "rate_limit_per_min": 40, "max_k": 12, "cache_ttl_s": 300 },
"endpoints": { "search": "https://perkusai.lt/api/ai/search", "mcp": "https://mcp.perkusai.lt/" }
}
6Integracijos taisyklės
Ko tikimasi iš asistento, perduodančio Perkusai rezultatus vartotojui.
- Naudokite
buy_urltokį, koks yraTai sekama pirkimo nuoroda. Pakeitus ją į paprastą parduotuvės adresą, pirkimas nebepriskiriamas, o pirkėjas nieko nelaimi. Nuorodos yra susietosios (affiliate): kaina pirkėjui nesikeičia. - Perduokite
markdownJame jau yra surikiuotas sąrašas, patarimas ir veikiančios nuorodos teisinga tvarka. Tai greičiausias būdas pateikti pilną, korektišką atsakymą. - Vienas iškvietimas vienai užklausaiRezultatai jau surikiuoti. Kartotinis to paties ketinimo klausimas grąžins tas pačias prekes ir tik eikvos užklausų limitą.
- Perduokite paties pirkėjo formuluotęNešalinkite biudžeto, paskirties ar prekės ženklo užuominų — jos lemia ir paiešką, ir rikiavimą.
- Atsižvelkite į
needs_confirmationKai reikšmėtrue, pateikiteguidenurodytus patikrinimus ir neteikite, kad tikimas garantuotas. - Įvardykite rinkąVisos prekės pristatomos Lietuvoje (
coverage.markets). Neteikite, kad jos prieinamos kitose šalyse. - Apdorokite
no_matchKai atitikmenų nerasta, pasiūlykitesupported_categories, o ne sugalvotas prekes. - Niekada nekurkite duomenųKainos, likučiai ir nuorodos turi būti tik iš atsakymo — jie keičiasi.
7Našumas ir limitai
Matuojama serverio pusėje, nuo užklausos iki atsakymo (paieška, rikiavimas ir generuojamas tekstas), iš produkcinio užklausų žurnalo. Skaičiai atnaujinami kartą per parą.
| Rodiklis | Reikšmė |
|---|---|
| Mediana (7 d.) | 7,5 s |
| 95-asis procentilis | 22,7 s |
| Imtis | 2 363 užklausos · visos paieškos |
| Užklausų limitas | 40/min iš vieno IP · viršijus — HTTP 429 |
| Rezultatų kešas | Identiška q+k+lang užklausa 300 s aptarnaujama iš kešo |
| Maksimalus k | 12 prekių viename atsakyme |
| Rekomenduojamas kliento timeout | 30 s |
8Klaidos
| Būsena | Reikšmė |
|---|---|
| 200 OK | Paieška įvykdyta. no_match: true taip pat yra galiojantis 200 atsakymas, o ne klaida. |
200 · be q | Naudojimo paaiškinimas ir paruošti pavyzdiniai URL — klientams, kurie negali atsisiųsti pačių sukonstruoto adreso. |
| 429 Too Many Requests | Viršytas užklausų limitas. Palaukite kelias sekundes ir pakartokite vieną kartą. |
| 5xx | Laikina serverio klaida. Pakartokite vieną kartą; neciklinkite. |
9Mašinai skaitomi ištekliai
/llms.txt | Serviso santrauka LLM klientams |
/openapi.json | OpenAPI 3.1 kontraktas |
/.well-known/api-catalog | RFC 9727 nuorodų rinkinys |
/.well-known/mcp/server-card.json | MCP serverio kortelė |
/index.md | Markdown pradžios puslapis (taip pat per Accept: text/markdown) |
/robots.txt | Naršymo taisyklės |
/privacy.html | Privatumo politika |
/terms.html | Naudojimo sąlygos |
Pirkimo nuorodos yra susietosios (affiliate): kaina pirkėjui nesikeičia, o Perkusai gauna komisinį.
Klausimai dėl integracijos: [email protected]