Kümeler
Küme, kaydedilmiş bir web sitesi listesidir. API üzerinden bir aramadan ya da kendi listenizden küme oluşturabilir, kümeleri birleştirebilir ve bir kümedeki her sitenin sayfa kaynağından değerler çıkarabilirsiniz: iletişim bilgileri, sosyal medya profilleri, analiz ve etiket kimlikleri, bir düzenli ifadenin eşleşebileceği her şey.
Uç noktalar
| Uç nokta | Yöntem | Ne yapar |
|---|---|---|
/v1/clusters | GET | Hesabın kümeleri, en yenisi önce: kimlik, ad, alan adı sayısı, oluşturulma zamanı. |
/v1/clusters | POST | Bir aramadan (query) ya da bir listeden (domains) küme oluşturur; name isteğe bağlıdır. |
/v1/clusters/{id} | GET | Küme ve alan adlarından bir sayfa: page, per_page (en fazla 10.000), her satıra bir alan adı için format=txt. |
/v1/clusters/combine | POST | Mevcut kümelerden yeni bir küme: operation and, or ya da diff, clusters ise kimlik listesi. |
/v1/clusters/{id}/extract | GET, POST | presets ve regex ile parça parça değer çıkarır: offset, limit (çağrı başına en fazla 1.000 site), format json, xml ya da csv. |
/v1/clusters/presets | GET | Hazır ifadeler ve kalıpları. |
/v1/clusters/{id}/rename | POST | Yeni name. |
/v1/clusters/{id}/delete | POST | Kümeyi kalıcı olarak siler. |
Bir kümeyi değiştiren her işlem JSON gövdeli bir POST isteğidir; PUT ya da
DELETE yoktur, böylece arama yapabilen her istemci kümeleri de yönetebilir.
Token, hız sınırı ve hata biçimi aramadakiyle
aynıdır; /v1/account yanıtındaki küme sayıları kaç kümeniz
olduğunu ve bugün kaç veri çıkarma puanınızın kaldığını gösterir.
Küme oluşturma
Bir aramadan: sorgunun sonuçları, planınızın arama başına satır sınırına
kadar, kümeye dönüşür. /v1/search gibi bir arama harcar.
curl https://api.publicwww.com/v1/clusters \
-H "Authorization: Bearer $PUBLICWWW_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "\"googletagmanager.com/gtm.js\"", "name": "GTM sites"}'
{"id": 7, "name": "GTM sites", "size": 100000, "created": "2026-10-10T20:21:30Z",
"query": "\"googletagmanager.com/gtm.js\"", "total": 2412577, "index_complete": true}
Bir listeden: JSON dizisi olarak ya da her satırda bir tane olacak
şekilde alan adları veya URL'ler. Yalnızca dizinde bulunan siteler tutulur:
submitted kaç tane gönderdiğinizi, size kaçının
kümeye girdiğini gösterir. Liste hiçbir şey harcamaz.
curl https://api.publicwww.com/v1/clusters \
-H "Authorization: Bearer $PUBLICWWW_KEY" \
-H "Content-Type: application/json" \
-d '{"domains": ["example.com", "https://www.example.org/about"], "name": "Prospects"}'
Plan, bir kümedeki alan adı sayısını sınırlar; bir hesap da en fazla 100 küme
saklar. Zaten 100 küme varken yenisini oluşturmak 409 cluster_limit
yanıtını alır: sizin yerinize hiçbir şey silinmez, önce artık gerekmeyen
kümeleri silin.
Kümeleri birleştirme
curl https://api.publicwww.com/v1/clusters/combine \
-H "Authorization: Bearer $PUBLICWWW_KEY" \
-H "Content-Type: application/json" \
-d '{"operation": "diff", "clusters": [7, 3], "name": "GTM, not yet contacted"}'
and tüm kümelerde bulunan alan adlarını, or herhangi
birinde bulunanları, diff ise birinci kümede olup ikincide
olmayanları tutar (bu durumda tam iki küme verilir). Birleştirme hiçbir şey
harcamaz.
Veri çıkarma
Veri çıkarma, kümedeki her sitenin dizine eklenmiş sayfalarını okur ve ifadelerin yakaladıklarını her ifade için bir sütun olarak döndürür. En kolay yol bir hazır kalıptır:
| Hazır kalıp | Neyi çıkarır |
|---|---|
email | mailto: bağlantılarındaki adresler |
phone | tel: bağlantılarındaki numaralar |
whatsapp, telegram, skype | Kendi bağlantılarından WhatsApp numaraları, Telegram kullanıcı adları, Skype adları |
facebook, instagram, twitter, linkedin | Sitenin sosyal medya profillerine bağlantılar (twitter x.com'u, linkedin şirket sayfalarını da alır) |
gtm, ga4, ua | Google Tag Manager kapsayıcısı, Google Analytics 4 ve Universal Analytics kimlikleri |
hotjar | Hotjar site kimliği |
adsense | AdSense yayıncı kimliği |
bitcoin | bitcoin: ödeme bağlantılarındaki adresler |
Ya da kendi ifadenizi yazın: eğik çizgiler (ya da dikey çizgiler) arasında,
isteğe bağlı i, m, s, u
bayraklarıyla, en fazla 200 karakterlik bir düzenli ifade. Değer, ilk yakalama
grubudur; snipexp: ile aynı
kural. Hazır kalıplar dahil, çağrı başına en fazla on ifade kullanılabilir.
curl https://api.publicwww.com/v1/clusters/7/extract \
-H "Authorization: Bearer $PUBLICWWW_KEY" \
-H "Content-Type: application/json" \
-d '{"presets": ["gtm", "email"], "regex": ["/data-site-id=\"([0-9]+)\"/i"], "limit": 1000}'
{
"cluster": 7, "name": "GTM sites", "size": 100000,
"offset": 0, "scanned": 1000, "in_index": 1000, "with_matches": 941,
"next_offset": 1000,
"regex": ["/(GTM-[A-Z0-9]{4,10})\\b/", "/mailto:(...)/i", "/data-site-id=\"([0-9]+)\"/i"],
"points_used": 1834.2, "points_left": 98165,
"rows": [
{ "domain": "example.com", "values": [["GTM-AB12CD"], ["info@example.com"], []], "matches": 2 }
]
}
Parça parça. Bir çağrı, offset değerinden başlayarak
kümenin en fazla 1.000 sitesini işler. Yanıt null dönene kadar,
offset değerini next_offset yaparak yeniden çağırın.
Hiçbir şeyin eşleşmediği siteler yanıta alınmaz; skip_empty=0
bunları da listeler. format=csv her site için bir satır verir
(önce alan adı, sonra her ifade için bir sütun); sonraki offset
X-Next-Offset başlığındadır.
Puanlar. Veri çıkarma, planınızın günlük veri çıkarma hakkından harcar:
dizinde bulunan her site, üzerinde bulunan değer sayısı kadar puan harcar; hiç
değer bulunmazsa 0,1. Puanlar bittiğinde çağrı erken durur ve
"stopped": "extract_quota_exceeded" ile o ana kadar bulduklarını
döndürür; hiç puan kalmamışken yapılan çağrı 429 extract_quota_exceeded
alır. Bir ifadeyi önce küçük bir limit ile deneyin; bkz.
küme sınırları.
Kodla bütün bir küme
Bir aramadan küme oluşturun ve her sitenin çıkarılan değerlerini bir CSV dosyasına yazın. İstemci kütüphaneleri aynısını hazır fonksiyonlar ve bir komut satırı aracı olarak sunar.
Python
import csv, os, time, requests
KEY = os.environ["PUBLICWWW_KEY"]
BASE = "https://api.publicwww.com"
H = {"Authorization": "Bearer " + KEY}
def call(method, path, body=None):
while True:
r = requests.request(method, BASE + path, headers=H, json=body)
if r.status_code == 429 and r.json()["error"]["code"] == "too_many_requests":
time.sleep(int(r.headers.get("Retry-After", 30)))
continue
r.raise_for_status()
return r.json()
cluster = call("POST", "/v1/clusters", {"query": '"googletagmanager.com/gtm.js"'})
offset = 0
with open("extract.csv", "w", newline="") as f:
out = csv.writer(f)
while offset is not None:
part = call("POST", "/v1/clusters/%d/extract" % cluster["id"],
{"presets": ["gtm", "email"], "offset": offset})
for row in part["rows"]:
out.writerow([row["domain"]] + [" ".join(v) for v in row["values"]])
offset = part["next_offset"]
JavaScript (Node 18+)
const BASE = "https://api.publicwww.com";
const H = { Authorization: "Bearer " + process.env.PUBLICWWW_KEY,
"Content-Type": "application/json" };
async function call(method, path, body) {
for (;;) {
const r = await fetch(BASE + path, { method, headers: H,
body: body && JSON.stringify(body) });
const data = await r.json();
if (r.status === 429 && data.error.code === "too_many_requests") {
await new Promise(ok => setTimeout(ok, 1000 * (r.headers.get("Retry-After") || 30)));
continue;
}
if (!r.ok) throw new Error(data.error.message);
return data;
}
}
const cluster = await call("POST", "/v1/clusters", { query: '"hotjar.com"' });
for (let offset = 0; offset !== null; ) {
const part = await call("POST", `/v1/clusters/${cluster.id}/extract`,
{ presets: ["hotjar", "email"], offset });
for (const row of part.rows) console.log(row.domain, row.values.map(v => v.join(" ")).join(";"));
offset = part.next_offset;
}
PHP
<?php
function call ($method, $path, $body = null) {
$ch = curl_init ("https://api.publicwww.com" . $path);
curl_setopt_array ($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer " . getenv ("PUBLICWWW_KEY"),
"Content-Type: application/json"],
CURLOPT_POSTFIELDS => $body === null ? null : json_encode ($body),
]);
$data = json_decode (curl_exec ($ch), true);
if (isset ($data ["error"])) throw new Exception ($data ["error"]["message"]);
return $data;
}
$cluster = call ("POST", "/v1/clusters", ["query" => '"jquery.min.js"']);
$out = fopen ("extract.csv", "w");
for ($offset = 0; $offset !== null; ) {
$part = call ("POST", "/v1/clusters/" . $cluster ["id"] . "/extract",
["presets" => ["email", "phone"], "offset" => $offset]);
foreach ($part ["rows"] as $row)
fputcsv ($out, array_merge ([$row ["domain"]], array_map (fn ($v) => join (" ", $v), $row ["values"])));
$offset = $part ["next_offset"];
}
Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
func call(method, path string, body, out any) error {
b, _ := json.Marshal(body)
req, _ := http.NewRequest(method, "https://api.publicwww.com"+path, bytes.NewReader(b))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PUBLICWWW_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return fmt.Errorf("publicwww: %s", resp.Status)
}
return json.NewDecoder(resp.Body).Decode(out)
}
func main() {
var cluster struct{ ID int `json:"id"` }
if err := call("POST", "/v1/clusters", map[string]any{"query": `"googletagmanager.com/gtm.js"`}, &cluster); err != nil {
panic(err)
}
for offset := 0; ; {
var part struct {
Rows []struct {
Domain string `json:"domain"`
Values [][]string `json:"values"`
} `json:"rows"`
NextOffset *int `json:"next_offset"`
}
path := fmt.Sprintf("/v1/clusters/%d/extract", cluster.ID)
if err := call("POST", path, map[string]any{"presets": []string{"gtm", "ga4"}, "offset": offset}, &part); err != nil {
panic(err)
}
for _, r := range part.Rows {
cells := []string{r.Domain}
for _, v := range r.Values {
cells = append(cells, strings.Join(v, " "))
}
fmt.Println(strings.Join(cells, ";"))
}
if part.NextOffset == nil {
break
}
offset = *part.NextOffset
}
}
Ruby
require "json"
require "net/http"
def call(path, body)
uri = URI("https://api.publicwww.com" + path)
req = Net::HTTP::Post.new(uri, "Authorization" => "Bearer #{ENV.fetch('PUBLICWWW_KEY')}",
"Content-Type" => "application/json")
req.body = body.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
data = JSON.parse(res.body)
raise data["error"]["message"] if data["error"]
data
end
cluster = call("/v1/clusters", { query: '"hotjar.com"' })
offset = 0
while offset
part = call("/v1/clusters/#{cluster['id']}/extract", { presets: %w[hotjar email], offset: offset })
part["rows"].each { |r| puts [r["domain"], *r["values"].map { |v| v.join(" ") }].join(";") }
offset = part["next_offset"]
end
Hatalar
| Durum ve kod | Anlamı |
|---|---|
404 cluster_not_found | Bu hesapta bu kimliğe sahip bir küme yok. |
409 cluster_limit | Hesapta zaten 100 küme var. |
400 invalid_regex | Bir ifade, eğik ya da dikey çizgiler arasına yazılmış ve en fazla 200 karakterlik bir PCRE değil. |
400 unknown_preset | Böyle bir hazır kalıp yok; yanıt mevcut olanları listeler. |
400 missing_source, ambiguous_source | Küme oluşturmak için query ya da domains parametrelerinden yalnızca biri gerekir. |
429 extract_quota_exceeded | Bugünün veri çıkarma puanları tükendi. |
Tam liste hatalar sayfasında ve API'nin https://api.publicwww.com/ adresindeki kendi tanımında yer alır. Bir yapay zeka asistanında aynı işlemler MCP araçları olarak sunulur.
Sonraki Kod örnekleri