Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

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