# ThriftBooks Used Book Listings Scraper (`automation-lab/thriftbooks-used-book-listings-scraper`) Actor

📚 Extract public ThriftBooks used-book listings with price, condition, ISBN, format, availability, images, and canonical product URLs.

- **URL**: https://apify.com/automation-lab/thriftbooks-used-book-listings-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## ThriftBooks Used Book Listings Scraper

Extract public ThriftBooks browse listings for used-book sourcing, edition comparison, and inventory monitoring.

Turn a title, author, ISBN, or public ThriftBooks browse URL into structured listing records.

### What does it do?

This Actor collects the public listing cards returned by ThriftBooks browse searches.

It uses an HTTP-first parser, so it does not require a browser for the listing MVP.

Each record preserves the price and condition shown at the time of collection.

Use it to make a repeatable inventory snapshot instead of copying results by hand.

### Who is it for?

📚 Used-book dealers can compare available conditions and prices before sourcing stock.

🛒 Catalog and marketplace teams can normalize public listing data into their own product workflows.

📈 Price-monitoring teams can schedule the same input and compare snapshots over time.

🔎 Researchers can search an author, title, or ISBN and export the visible results.

### Why use it?

ThriftBooks results combine editions, formats, and conditions that are difficult to compare in a browser tab.

The dataset separates the values into fields that are easy to filter, join, and export.

Canonical product URLs remove result-tracking parameters, making deduplication simpler.

The source URL and collection timestamp make each snapshot traceable.

### Data you get

| Field | Description |
| --- | --- |
| `title` | Listing title shown on the browse card |
| `author` | Public author text when shown |
| `isbn` | ISBN from the product link when provided |
| `format` | Format such as Paperback or Hardcover |
| `condition` | Condition such as Good or Like New |
| `price` | Current displayed USD price |
| `listPrice` | Displayed list price when available |
| `availability` | `in_stock`, `out_of_stock`, or `unknown` |
| `imageUrl` | Cover image URL when available |
| `url` | Canonical ThriftBooks product URL |
| `searchUrl` | Browse page that produced the record |
| `sourceTimestamp` | ISO timestamp for the collected page |

### How do I search ThriftBooks listings?

1. Enter one or more book searches in **Book searches**.

2. Start with a low maximum listing count such as 20.

3. Run the Actor and open the default dataset.

4. Filter by `condition`, `format`, or `availability`.

5. Schedule the same input when you need repeat monitoring.

### Search input

Use `searchQueries` for titles, author names, ISBNs, or phrases.

For example, `harry potter` returns the public browse results for that phrase.

You can submit multiple queries in one run.

Duplicate canonical product URLs are emitted only once per run.

### Browse and category URLs

Use `startUrls` when you already have a public ThriftBooks browse or category URL.

This is useful when you selected filters on the site before building a workflow.

The Actor accepts either a URL object in the console editor or a plain URL string through the API.

Combine URLs with search queries when you need multiple inventory segments.

### Pagination limits

`maxPagesPerInput` controls how many public result pages are requested for each input.

A typical browse page can contain many listing cards.

The Actor stops early when a page is empty or only repeats prior listings.

Keep page limits conservative to reduce target load and make scheduled monitoring predictable.

### Output example

```json
{
  "title": "Harry Potter and the Sorcerer's Stone",
  "author": "J.K. Rowling",
  "isbn": "0590353403",
  "format": "Hardcover",
  "condition": "Like New",
  "price": 8.09,
  "listPrice": 29.99,
  "availability": "in_stock",
  "url": "https://www.thriftbooks.com/w/harry-potter-and-the-philosophers-stone-by-jk-rowling/248957/",
  "sourceTimestamp": "2026-07-11T00:00:00.000Z"
}
````

Values vary with the public listing card and may be absent when ThriftBooks does not show them.

### Condition and format signals

Condition and format are copied from the public result card rather than inferred.

This is important when comparing books with the same title but different editions.

Treat missing values as source omissions, not as a claim about a book.

Use the canonical URL when you need to manually verify a high-value result.

### Availability and stock monitoring

The Actor reports availability based on visible browse-card signals.

`in_stock` means a current displayed price was found without an out-of-stock marker.

`out_of_stock` is used when visible source text says a listing is unavailable.

`unknown` is retained when the card does not provide a reliable signal.

### Pricing: how much does it cost to scrape ThriftBooks listings?

This Actor uses pay-per-event billing: a small start event and a per-listing event.

The current price is shown in the Apify console before you run it.

Start with a small sample to validate the fields you need.

Larger monitoring jobs should use only the pages required by your workflow.

### Tips for better results

🔎 Search an ISBN when you want the narrowest edition-oriented result set.

📚 Search an author when you are comparing a broader catalog.

🧪 Begin with one page before scheduling a multi-page job.

🕒 Save `sourceTimestamp` if your downstream system compares price changes.

### Integrations

Send the dataset to Google Sheets to compare a buyer's sourcing shortlist.

Load records into a database and deduplicate with `url` for catalog maintenance.

Use Make or Zapier to notify a team when a scheduled result enters a target price range.

Feed the canonical URLs into a review queue for manual edition verification.

### API usage with Node.js

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/thriftbooks-used-book-listings-scraper').call({
  searchQueries: ['harry potter'], maxItems: 20, maxPagesPerInput: 1,
});
console.log(await client.dataset(run.defaultDatasetId).listItems());
```

### API usage with Python

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("automation-lab/thriftbooks-used-book-listings-scraper").call(
    run_input={"searchQueries": ["harry potter"], "maxItems": 20, "maxPagesPerInput": 1}
)
print(client.dataset(run["defaultDatasetId"]).list_items().items)
```

### API usage with cURL

```bash
curl "https://api.apify.com/v2/acts/automation-lab~thriftbooks-used-book-listings-scraper/runs?token=$APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"searchQueries":["harry potter"],"maxItems":20,"maxPagesPerInput":1}'
```

### MCP with Claude

Add the Apify MCP server in Claude Code, then enable this Actor as a tool.

```bash
claude mcp add --transport http apify https://mcp.apify.com?tools=automation-lab/thriftbooks-used-book-listings-scraper
```

For Claude Desktop, add this server configuration and restart the app.

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/thriftbooks-used-book-listings-scraper"
    }
  }
}
```

Example prompt: “Search public ThriftBooks listings for ISBN 0590353403 and return condition and price.”

Example prompt: “Monitor public ThriftBooks listings for this author and summarize the cheapest in-stock formats.”

### Reliability and partial results

A later page can fail because public retail sites may throttle or change markup.

The Actor keeps records collected from earlier pages and logs the failed page.

It does not retry aggressively or use product-detail requests in the listing MVP.

If a source changes its public card structure, inspect logs and rerun a small sample first.

### Limitations

This Actor extracts public browse-card data; it does not promise a complete site catalog.

Product-detail-only fields such as full descriptions or publisher metadata are not part of this version.

Ratings and review counts are optional because they are not present on every browse card.

Prices, availability, and condition are snapshots and can change after collection.

### Legality and responsible use

Only collect public data you are authorized to access.

Respect ThriftBooks terms, robots guidance where applicable, and applicable privacy and data-use laws.

Use low page limits and avoid unnecessary repeated runs.

Do not use this Actor to bypass access controls or collect personal data.

### Troubleshooting: no items

Check that each URL is a public ThriftBooks browse or category page.

For search input, try a broad title or author before using an uncommon ISBN.

If a result page has no listing cards, the Actor stops that input to avoid repeated empty requests.

Review the run log for the exact source URL and response status.

### Troubleshooting: missing fields

Browse cards do not always display every field for every book.

A missing ISBN, rating, review count, or list price means the public card did not expose it.

Do not treat optional missing fields as zero values.

Use the record URL for manual verification when an exact edition matters.

### FAQ

**Does this scrape product detail pages?** No. This version intentionally focuses on public browse listings.

**Can I use multiple searches?** Yes. Add several `searchQueries` values in one run.

**Can I monitor prices over time?** Yes. Schedule an identical input and compare `price`, `availability`, and `sourceTimestamp`.

**Why did the run stop before my page limit?** It stops on an empty, repeated, or failed page to preserve partial results and avoid unnecessary requests.

### Related scrapers

For a broader automation portfolio, browse [Automation Lab Actors](https://apify.com/automation-lab).

Use a source-specific Actor when your workflow requires another marketplace rather than public ThriftBooks listings.

Keep each source separate so conditions, prices, and URL conventions remain clear.

### Support and feedback

Include the source URL, input, and a small output sample when reporting a parsing issue.

That information makes markup changes easier to diagnose without reproducing a large run.

For best reliability, validate a small input after changing a complex browse filter.

### Version notes

Version 0.1 focuses on public listing-card extraction and bounded pagination.

Future releases may add optional enrichment only when it can be measured without reducing listing reliability.

Stable field names are intended to make scheduled exports easier to maintain.

### Quick checklist

✅ Provide a search query or public browse/category URL.

✅ Keep the first run small.

✅ Verify condition, format, and availability fields for your target listings.

✅ Schedule the validated input for monitoring.

# Actor input Schema

## `searchQueries` (type: `array`):

Book titles, authors, ISBNs, or other search terms. Each term opens a public ThriftBooks browse search.

## `startUrls` (type: `array`):

Optional public ThriftBooks browse/category URLs. Use this for an already-filtered result page; it can be combined with searches.

## `maxItems` (type: `integer`):

Stop after this many unique listings across all inputs. Keep the first run small while validating your workflow.

## `maxPagesPerInput` (type: `integer`):

Maximum public result pages to request for each search or URL. Pagination stops early when a page is empty or repeats listings.

## Actor input object example

```json
{
  "searchQueries": [
    "harry potter"
  ],
  "startUrls": [],
  "maxItems": 20,
  "maxPagesPerInput": 1
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "harry potter"
    ],
    "startUrls": [],
    "maxItems": 20,
    "maxPagesPerInput": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/thriftbooks-used-book-listings-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "searchQueries": ["harry potter"],
    "startUrls": [],
    "maxItems": 20,
    "maxPagesPerInput": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/thriftbooks-used-book-listings-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "searchQueries": [
    "harry potter"
  ],
  "startUrls": [],
  "maxItems": 20,
  "maxPagesPerInput": 1
}' |
apify call automation-lab/thriftbooks-used-book-listings-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=automation-lab/thriftbooks-used-book-listings-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "ThriftBooks Used Book Listings Scraper",
        "description": "📚 Extract public ThriftBooks used-book listings with price, condition, ISBN, format, availability, images, and canonical product URLs.",
        "version": "0.1",
        "x-build-id": "PVQidBznYQeyPMegv"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/automation-lab~thriftbooks-used-book-listings-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-automation-lab-thriftbooks-used-book-listings-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/automation-lab~thriftbooks-used-book-listings-scraper/runs": {
            "post": {
                "operationId": "runs-sync-automation-lab-thriftbooks-used-book-listings-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/automation-lab~thriftbooks-used-book-listings-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-automation-lab-thriftbooks-used-book-listings-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "searchQueries": {
                        "title": "🔎 Book searches",
                        "type": "array",
                        "description": "Book titles, authors, ISBNs, or other search terms. Each term opens a public ThriftBooks browse search.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "startUrls": {
                        "title": "📚 Browse or category URLs",
                        "type": "array",
                        "description": "Optional public ThriftBooks browse/category URLs. Use this for an already-filtered result page; it can be combined with searches.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "maxItems": {
                        "title": "Maximum listings",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Stop after this many unique listings across all inputs. Keep the first run small while validating your workflow.",
                        "default": 20
                    },
                    "maxPagesPerInput": {
                        "title": "Maximum pages per input",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "Maximum public result pages to request for each search or URL. Pagination stops early when a page is empty or repeats listings.",
                        "default": 1
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
