# Website URL Crawler & Link Extractor (`maximedupre/website-url-crawler`) Actor

Crawl JavaScript-rendered websites and export a URL link map. Get source pages, depth, anchor text, link type, HTTP metadata, and crawl status.

- **URL**: https://apify.com/maximedupre/website-url-crawler.md
- **Developed by:** [Maxime Dupré](https://apify.com/maximedupre) (community)
- **Categories:** Developer tools, SEO tools, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.80 / 1,000 discovered website links

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

### 🔗 Website URL crawler for rendered pages

Website URL Crawler crawls JavaScript-rendered public websites and exports a clean link map. Add one or more website URLs or domains, and the Actor opens pages in a browser, reads the rendered links, follows the pages you allow, and saves one dataset item per discovered link.

Use it for SEO audits, website migrations, QA checks, broken-link investigation, internal linking reviews, and RAG source inventories. It works well when you need more than a raw list of URLs: each link keeps its source page, parent URL, depth, anchor text, link type, crawl status, and optional HTTP metadata.

For a quick first run, keep the prefilled [IANA reserved domains page](https://www.iana.org/domains/reserved). It is small, public, and gives you a readable website link map without needing your own test site.

### 🧭 What this Actor does

Website URL Crawler starts from your submitted websites and discovers links from rendered HTML pages. That means links added by common client-side JavaScript can be included in the crawl output after the page loads.

The Actor can crawl within the same host, within the same registrable domain, or emit external links as discovered-only rows. Only internal page links are followed further. Document and media links can be included or skipped depending on the asset setting you choose.

Each run is designed for link extraction and crawl mapping, not full content scraping. The output helps you answer practical questions such as:

- Which pages does this website link to?
- Where was each URL found?
- What anchor text points to each link?
- How deep is the link from the start page?
- Is the link internal, external, a document, or an asset?
- Which crawled pages returned HTTP status and content type metadata?

### 📦 Data you get

Every saved item represents one crawled or discovered website link. Fields include:

- `startUrl` - the original website URL this crawl started from
- `url` and `normalizedUrl` - the discovered link and its normalized version
- `sourceUrl` - the rendered page where the link was found
- `parentUrl` - the page that led to a crawled URL, when available
- `depth` - crawl depth from the start URL
- `anchorText` - visible link text when present
- `linkType` - page, document, asset, or external
- `crawlStatus` - crawled or discovered
- `httpStatusCode`, `finalUrl`, and `contentType` - when HTTP status checks are enabled and the page is crawled
- `isInternal`, `isExternal`, `isAsset`, and `isDuplicate` - booleans for filtering and audits
- `rawHref`, `foundOnTitle`, `sourceIndex`, and `discoveredAt` - source evidence and scrape metadata

You can export the dataset from Apify as JSON, CSV, Excel, XML, RSS, or HTML, or consume it through the Apify API, schedules, webhooks, and integrations.

### ⚙️ How to run it

1. Add one or more website URLs or domains.
2. Choose how many pages to crawl per website.
3. Set the crawl depth and maximum links per page.
4. Pick whether to stay on the same host, same domain, or include external links as discovered-only rows.
5. Choose whether to include document links, all asset links, or pages only.
6. Run the Actor and open the dataset.

Domains such as `example.com` are accepted and normalized to HTTPS. Full URLs such as `https://example.com/docs` are also accepted.

### 🧾 Input options

`Website URLs` is the only required input. Add the sites you want to crawl.

`Max pages per website` controls how many HTML pages are opened for each start URL. Discovered links can still be emitted before the page cap is reached.

`Max crawl depth` controls how many levels of links the Actor follows from the start page. Use `0` when you only want links from the submitted page itself.

`Max links per page` limits how many rendered links are emitted and considered from each crawled page.

`Crawl scope` controls which internal links can be followed. External links are never crawled further; they can be emitted as discovered-only rows when your settings allow it.

`Asset links` controls whether the dataset includes only HTML page links, document links such as PDFs and spreadsheets, or all links including media assets.

`Ignored extensions` lets you skip common file types unless you choose to include all links.

`Check HTTP status` adds status code, final URL, and content type for crawled pages.

### 🧪 Output example

```json
{
  "startUrl": "https://www.iana.org/domains/reserved",
  "url": "https://www.iana.org/domains/root",
  "normalizedUrl": "https://www.iana.org/domains/root",
  "sourceUrl": "https://www.iana.org/domains/reserved",
  "parentUrl": "https://www.iana.org/domains/reserved",
  "depth": 1,
  "anchorText": "Root Zone Management",
  "linkType": "page",
  "crawlStatus": "discovered",
  "isInternal": true,
  "isExternal": false,
  "isAsset": false,
  "isDuplicate": false,
  "rawHref": "/domains/root",
  "foundOnTitle": "IANA-managed Reserved Domains",
  "sourceIndex": 24,
  "discoveredAt": "2026-05-26T00:00:00.000Z"
}
````

### 💳 Pricing

This Actor uses pay-per-event pricing. You are charged for each saved website link item. The pricing event is called `Discovered website link`.

Use a small `Max pages per website` value for your first run, then increase the limit once the output shape looks right.

### ⚠️ Limits and caveats

Website URL Crawler is browser-rendered, so it is designed for capability over minimum runtime cost. Large sites can produce many links quickly; start with a small page limit and expand from there.

The Actor reads links from public rendered pages. It does not log in, submit forms, click through interactive menus, or guarantee that every route in a single-page app is discoverable from normal anchor links.

HTTP status, final URL, and content type are available for crawled pages. Links that are only discovered but not crawled are still useful for mapping, but they may not have those HTTP fields.

### ❓ FAQ

#### 🌐 Does this crawl JavaScript-rendered websites?

Yes. Pages are opened in a browser and links are extracted from the rendered page, not only the initial HTML response.

#### 🌍 Will it crawl external websites too?

No. External links can be saved as discovered links, but the crawler only follows internal page links within the scope you choose.

#### 📄 Can I crawl only one page?

Yes. Set `Max crawl depth` to `0` when you want links from the submitted page without following deeper links.

#### 🧯 Is this a broken link checker?

It can help with broken-link workflows by exporting discovered links and HTTP metadata for crawled pages, but the core output is a website URL crawl map.

### 📝 Changelog

- 0.0: Initial release.

### 🆘 Support

For issues, questions, or feature requests, [file a ticket](https://console.apify.com/actors/maximedupre~website-url-crawler/issues) and I'll fix or implement it in less than 24h 🫡

### 🔗 Other actors

- [Website Emails Scraper ↗](https://apify.com/maximedupre/website-emails-scraper) - Find public email addresses on the websites you already crawl.
- [Font Detector ↗](https://apify.com/maximedupre/font-detector) - Audit fonts, font files, and typography metadata from public pages.
- [Business Address Scraper ↗](https://apify.com/maximedupre/business-address-scraper) - Extract physical business addresses from company websites.
- [Product Hunt Scraper ↗](https://apify.com/maximedupre/product-hunt-scraper) - Build startup lead lists and enrich launches with website details.
- [LinkedIn Company Scraper ↗](https://apify.com/maximedupre/linkedin-company-scraper) - Export public company profile data for lead and market research.

**Made with ❤️ by Maxime Dupré**

# Actor input Schema

## `urls` (type: `array`):

Add one or more public website URLs or domains to crawl. Domains like example.com are accepted and normalized to HTTPS.

## `maxPagesPerStartUrl` (type: `integer`):

Maximum number of rendered HTML pages to open per input website. Discovered links can still be emitted before this cap is reached.

## `maxDepth` (type: `integer`):

How many link levels to follow from each start URL. Use 0 to extract links from the submitted page only.

## `maxLinksPerPage` (type: `integer`):

Maximum rendered links to emit and consider from each crawled page.

## `crawlScope` (type: `string`):

Choose which discovered links can be followed after each page renders. External links can be saved as discovered-only rows but are not crawled further.

## `assetPolicy` (type: `string`):

Choose whether to save document and media links. Only internal HTML page links are crawled further.

## `ignoredExtensions` (type: `array`):

Links ending with these extensions are skipped unless Asset links is set to Include all links.

## `includeHttpStatus` (type: `boolean`):

Include HTTP status code, final URL, and content type for crawled pages.

## `proxyConfiguration` (type: `object`):

Apify Proxy settings used to load rendered website pages. Keep the default unless the target site needs a specific proxy setup.

## Actor input object example

```json
{
  "urls": [
    {
      "url": "https://www.iana.org/domains/reserved"
    }
  ],
  "maxPagesPerStartUrl": 5,
  "maxDepth": 2,
  "maxLinksPerPage": 120,
  "crawlScope": "same-domain",
  "assetPolicy": "include-documents",
  "ignoredExtensions": [
    "jpg",
    "jpeg",
    "png",
    "gif",
    "webp",
    "svg",
    "zip",
    "mp4",
    "mp3"
  ],
  "includeHttpStatus": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset of crawled and discovered website links.

# 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 = {
    "urls": [
        {
            "url": "https://www.iana.org/domains/reserved"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maximedupre/website-url-crawler").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 = { "urls": [{ "url": "https://www.iana.org/domains/reserved" }] }

# Run the Actor and wait for it to finish
run = client.actor("maximedupre/website-url-crawler").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 '{
  "urls": [
    {
      "url": "https://www.iana.org/domains/reserved"
    }
  ]
}' |
apify call maximedupre/website-url-crawler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=maximedupre/website-url-crawler",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Website URL Crawler & Link Extractor",
        "description": "Crawl JavaScript-rendered websites and export a URL link map. Get source pages, depth, anchor text, link type, HTTP metadata, and crawl status.",
        "version": "0.0",
        "x-build-id": "L7pDYWhHfwUFoz9jZ"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/maximedupre~website-url-crawler/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-maximedupre-website-url-crawler",
                "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/maximedupre~website-url-crawler/runs": {
            "post": {
                "operationId": "runs-sync-maximedupre-website-url-crawler",
                "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/maximedupre~website-url-crawler/run-sync": {
            "post": {
                "operationId": "run-sync-maximedupre-website-url-crawler",
                "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",
                "required": [
                    "urls"
                ],
                "properties": {
                    "urls": {
                        "title": "Website URLs",
                        "minItems": 1,
                        "type": "array",
                        "description": "Add one or more public website URLs or domains to crawl. Domains like example.com are accepted and normalized to HTTPS.",
                        "items": {
                            "type": "object",
                            "properties": {
                                "url": {
                                    "title": "URL",
                                    "type": "string",
                                    "description": "Website URL or domain to crawl, for example example.com or https://example.com/docs.",
                                    "minLength": 1
                                }
                            },
                            "required": [
                                "url"
                            ]
                        }
                    },
                    "maxPagesPerStartUrl": {
                        "title": "Max pages per website",
                        "minimum": 1,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Maximum number of rendered HTML pages to open per input website. Discovered links can still be emitted before this cap is reached.",
                        "default": 5
                    },
                    "maxDepth": {
                        "title": "Max crawl depth",
                        "minimum": 0,
                        "maximum": 30,
                        "type": "integer",
                        "description": "How many link levels to follow from each start URL. Use 0 to extract links from the submitted page only.",
                        "default": 2
                    },
                    "maxLinksPerPage": {
                        "title": "Max links per page",
                        "minimum": 1,
                        "maximum": 1000,
                        "type": "integer",
                        "description": "Maximum rendered links to emit and consider from each crawled page.",
                        "default": 120
                    },
                    "crawlScope": {
                        "title": "Crawl scope",
                        "enum": [
                            "same-host",
                            "same-domain",
                            "include-external-discovered"
                        ],
                        "type": "string",
                        "description": "Choose which discovered links can be followed after each page renders. External links can be saved as discovered-only rows but are not crawled further.",
                        "default": "same-domain"
                    },
                    "assetPolicy": {
                        "title": "Asset links",
                        "enum": [
                            "pages-only",
                            "include-documents",
                            "include-all-links"
                        ],
                        "type": "string",
                        "description": "Choose whether to save document and media links. Only internal HTML page links are crawled further.",
                        "default": "include-documents"
                    },
                    "ignoredExtensions": {
                        "title": "Ignored extensions",
                        "type": "array",
                        "description": "Links ending with these extensions are skipped unless Asset links is set to Include all links.",
                        "items": {
                            "type": "string"
                        },
                        "default": [
                            "jpg",
                            "jpeg",
                            "png",
                            "gif",
                            "webp",
                            "svg",
                            "zip",
                            "mp4",
                            "mp3"
                        ]
                    },
                    "includeHttpStatus": {
                        "title": "Check HTTP status",
                        "type": "boolean",
                        "description": "Include HTTP status code, final URL, and content type for crawled pages.",
                        "default": true
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Apify Proxy settings used to load rendered website pages. Keep the default unless the target site needs a specific proxy setup.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyCountry": "US"
                        }
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
