Skip to content

About

Official Spidra Swift SDK

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

Spidra Swift SDK

The official Swift SDK for Spidra that allows you to scrape pages, run browser actions, batch-process URLs, and crawl entire sites using modern Swift Concurrency. All results come back as structured data ready to feed into your iOS, macOS, or server-side Swift applications.

Installation

Swift Package Manager

Add Spidra to your Package.swift dependencies:

dependencies: [
    .package(url: "https://github.com/spidra-io/spidra-swift.git", from: "1.0.0")
]

Or add it directly via Xcode: File > Add Packages... and paste the repository URL.

Quick Start

import Spidra

Task {
    do {
        let spidra = SpidraClient(apiKey: "spd_YOUR_API_KEY")
        
        let params = ScrapeParams(
            urls: [ScrapeUrl(url: "https://news.ycombinator.com")],
            prompt: "List the top 5 stories with title, points, and comment count",
            output: "json"
        )
        
        let job = try await spidra.scrape.run(params)
        
        // Print the extracted content
        if let content = job.result?.content?.value {
            print(content)
        }
    } catch {
        print("Error: \(error.localizedDescription)")
    }
}

Table of Contents

Scraping

All scrape jobs run asynchronously using Swift's async/await. The run() method submits a job and polls until it finishes. Up to 3 URLs can be passed per request and they are processed in parallel.

Basic Scrape

let params = ScrapeParams(
    urls: [ScrapeUrl(url: "https://example.com/pricing")],
    prompt: "Extract all pricing plans with name, price, and included features",
    output: "json"
)

let job = try await spidra.scrape.run(params)
print(job.result?.content?.value ?? "No data")

Structured Output with JSON Schema

When you need a guaranteed shape, pass a schema. The API will enforce the structure and return null for missing fields rather than hallucinating values.

let schemaDict: [String: Any] = [
    "type": "object",
    "required": ["title", "company", "remote"],
    "properties": [
        "title": ["type": "string"],
        "company": ["type": "string"],
        "remote": ["type": ["boolean", "null"]]
    ]
]

let params = ScrapeParams(
    urls: [ScrapeUrl(url: "https://jobs.example.com/senior-engineer")],
    prompt: "Extract the job listing details",
    output: "json",
    schema: AnyCodable(schemaDict)
)

let job = try await spidra.scrape.run(params)

Geo-targeted Scraping

Pass useProxy: true and a proxyCountry code to route the request through a specific country. Useful for geo-restricted content.

let params = ScrapeParams(
    urls: [ScrapeUrl(url: "https://www.amazon.de/gp/bestsellers")],
    prompt: "List the top 10 products",
    useProxy: true,
    proxyCountry: "de"
)

Authenticated Pages

Pass cookies as a string to scrape pages that require a login session.

let params = ScrapeParams(
    urls: [ScrapeUrl(url: "https://app.example.com/dashboard")],
    prompt: "Extract the monthly revenue",
    cookies: "session=abc123; auth_token=xyz789"
)

Browser Actions

Actions let you interact with the page before the scrape runs. They execute in order.

let url = ScrapeUrl(
    url: "https://example.com/products",
    actions: [
        .click(selector: "#accept-cookies", value: nil),
        .wait(duration: 1000),
        .scroll(to: "80%")
    ]
)

let params = ScrapeParams(urls: [url], prompt: "Extract product names and prices")
let job = try await spidra.scrape.run(params)

Available actions:

  • .click(selector:value:)
  • .type(selector:value:)
  • .check(selector:value:)
  • .uncheck(selector:value:)
  • .wait(duration:)
  • .scroll(to:)
  • .forEach(observe:mode:...)

forEach: Process Every Element on a Page

forEach finds a set of elements and processes each individually. Best used when dealing with pagination, clicking into detail pages, or looping over long lists.

let forEachAction = BrowserAction.forEach(
    observe: "Find all book cards in the product grid",
    mode: "inline",
    captureSelector: "article.product_pod",
    maxItems: 20,
    itemPrompt: "Extract title, price, and star rating. Return as JSON",
    waitAfterClick: nil,
    actions: nil,
    pagination: nil
)

let url = ScrapeUrl(
    url: "https://books.toscrape.com/",
    actions: [forEachAction]
)

Modes available:

  • inline: Read element content directly without navigating.
  • navigate: Follow each element's link to its destination page and capture content there.
  • click: Click each element, capture the content that appears (e.g., a modal), then move on.

You can also use pagination to navigate through multiple pages automatically:

let pagination = BrowserActionPagination(nextSelector: "li.next > a", maxPages: 3)

Manual Job Control

Use submit() and get() when you want to manage polling yourself.

// Submit a job immediately
let queued = try await spidra.scrape.submit(ScrapeParams(
    urls: [ScrapeUrl(url: "https://example.com")],
    prompt: "Extract the main headline"
))

// Check status later
let status = try await spidra.scrape.get(queued.jobId)
if status.status == "completed" {
    print(status.result?.content?.value ?? "")
}

Poll Options

Override default polling intervals via PollOptions:

let options = PollOptions(pollInterval: 2.0, timeout: 60.0)
let job = try await spidra.scrape.run(params, options: options)

Batch Scraping

Submit up to 50 URLs in a single request. All URLs are processed in parallel.

let params = BatchScrapeParams(
    urls: [
        "https://shop.example.com/product/1",
        "https://shop.example.com/product/2",
        "https://shop.example.com/product/3"
    ],
    prompt: "Extract product name, price, and availability",
    output: "json",
    useProxy: true
)

let batch = try await spidra.batch.run(params)

for item in batch.items {
    if item.status == "completed" {
        print("Completed: \(item.url)")
    } else if item.status == "failed" {
        print("Failed: \(item.error ?? "Unknown")")
    }
}

You can also list(), retry(), or cancel() batches.

Crawling

Given a starting URL, Spidra discovers pages automatically according to your instruction and extracts structured data from each one.

let params = CrawlParams(
    baseUrl: "https://competitor.com/blog",
    crawlInstruction: "Find all blog posts published in 2024",
    transformInstruction: "Extract the title, author, publish date",
    maxPages: 30
)

let job = try await spidra.crawl.run(params)

if let pages = job.result {
    for page in pages {
        print(page.url, page.data?.value ?? "No Data")
    }
}

Fetch signed download URLs for HTML and Markdown for all crawled pages:

let response = try await spidra.crawl.pages(job.jobId)

Logs

Scrape logs are stored for every job that runs through the API.

let params = ScrapeLogsParams(status: "failed", limit: 20)
let response = try await spidra.logs.list(params)

for log in response.logs {
    print("Log: \(log.uuid) - Status: \(log.status) - Credits: \(log.creditsUsed)")
}

// Get full extraction result for a specific log
let detail = try await spidra.logs.get("log-uuid")

Usage Statistics

Returns credit and request usage broken down by day or week.

let rows = try await spidra.usage.get("30d") // "7d" | "30d" | "weekly"

for row in rows {
    print("Date: \(row.date) - Requests: \(row.requests) - Credits: \(row.credits)")
}

Error Handling

Every API error throws a SpidraError. Catch the specific case you care about.

do {
    let job = try await spidra.scrape.run(params)
} catch SpidraError.authenticationError(let msg) {
    // 401: API key is missing or invalid
    print("Check your API key: \(msg)")
} catch SpidraError.insufficientCreditsError(let msg) {
    // 403: Monthly credit limit reached
    print("Out of credits: \(msg)")
} catch SpidraError.rateLimitError(let msg) {
    // 429: Too many requests
    print("Rate limited: \(msg)")
} catch SpidraError.serverError(let msg) {
    // 500: Server error
    print("Server error: \(msg)")
} catch {
    // Decoding errors, network timeouts, etc.
    print("Other error: \(error.localizedDescription)")
}

Requirements

  • Swift 5.9+
  • iOS 15.0+ / macOS 12.0+ / tvOS 15.0+ / watchOS 8.0+
  • A Spidra API key (sign up free)

License

MIT

About

Official Spidra Swift SDK

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages