xtrawl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCUMENTATION.md +774 -0
- package/README.md +232 -0
- package/dist/auth/bootstrap.d.ts +4 -0
- package/dist/auth/bootstrap.d.ts.map +1 -0
- package/dist/auth/bootstrap.js +29 -0
- package/dist/auth/bootstrap.js.map +1 -0
- package/dist/auth/cookies.d.ts +7 -0
- package/dist/auth/cookies.d.ts.map +1 -0
- package/dist/auth/cookies.js +83 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/loaders.d.ts +6 -0
- package/dist/auth/loaders.d.ts.map +1 -0
- package/dist/auth/loaders.js +69 -0
- package/dist/auth/loaders.js.map +1 -0
- package/dist/auth/material.d.ts +3 -0
- package/dist/auth/material.d.ts.map +1 -0
- package/dist/auth/material.js +20 -0
- package/dist/auth/material.js.map +1 -0
- package/dist/auth/records.d.ts +4 -0
- package/dist/auth/records.d.ts.map +1 -0
- package/dist/auth/records.js +38 -0
- package/dist/auth/records.js.map +1 -0
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +30 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +85 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/parser.d.ts +22 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +275 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/client/client.d.ts +29 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +192 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/collectors.d.ts +15 -0
- package/dist/client/collectors.d.ts.map +1 -0
- package/dist/client/collectors.js +185 -0
- package/dist/client/collectors.js.map +1 -0
- package/dist/client/database.d.ts +56 -0
- package/dist/client/database.d.ts.map +1 -0
- package/dist/client/database.js +146 -0
- package/dist/client/database.js.map +1 -0
- package/dist/client/profiles.d.ts +5 -0
- package/dist/client/profiles.d.ts.map +1 -0
- package/dist/client/profiles.js +36 -0
- package/dist/client/profiles.js.map +1 -0
- package/dist/client/search.d.ts +21 -0
- package/dist/client/search.d.ts.map +1 -0
- package/dist/client/search.js +139 -0
- package/dist/client/search.js.map +1 -0
- package/dist/client/types.d.ts +21 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config/defaults.d.ts +3 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +38 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/environment.d.ts +5 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +62 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validation.d.ts +6 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +147 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/domain/accounts.d.ts +64 -0
- package/dist/domain/accounts.d.ts.map +1 -0
- package/dist/domain/accounts.js +2 -0
- package/dist/domain/accounts.js.map +1 -0
- package/dist/domain/errors.d.ts +61 -0
- package/dist/domain/errors.d.ts.map +1 -0
- package/dist/domain/errors.js +89 -0
- package/dist/domain/errors.js.map +1 -0
- package/dist/domain/http.d.ts +36 -0
- package/dist/domain/http.d.ts.map +1 -0
- package/dist/domain/http.js +2 -0
- package/dist/domain/http.js.map +1 -0
- package/dist/domain/manifest.d.ts +23 -0
- package/dist/domain/manifest.d.ts.map +1 -0
- package/dist/domain/manifest.js +2 -0
- package/dist/domain/manifest.js.map +1 -0
- package/dist/domain/records.d.ts +67 -0
- package/dist/domain/records.d.ts.map +1 -0
- package/dist/domain/records.js +2 -0
- package/dist/domain/records.js.map +1 -0
- package/dist/domain/requests.d.ts +87 -0
- package/dist/domain/requests.d.ts.map +1 -0
- package/dist/domain/requests.js +2 -0
- package/dist/domain/requests.js.map +1 -0
- package/dist/engine/api-engine.d.ts +25 -0
- package/dist/engine/api-engine.d.ts.map +1 -0
- package/dist/engine/api-engine.js +98 -0
- package/dist/engine/api-engine.js.map +1 -0
- package/dist/engine/extractors.d.ts +27 -0
- package/dist/engine/extractors.d.ts.map +1 -0
- package/dist/engine/extractors.js +248 -0
- package/dist/engine/extractors.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest/default-manifest.d.ts +3 -0
- package/dist/manifest/default-manifest.d.ts.map +1 -0
- package/dist/manifest/default-manifest.js +46 -0
- package/dist/manifest/default-manifest.js.map +1 -0
- package/dist/manifest/model.d.ts +3 -0
- package/dist/manifest/model.d.ts.map +1 -0
- package/dist/manifest/model.js +48 -0
- package/dist/manifest/model.js.map +1 -0
- package/dist/manifest/provider.d.ts +19 -0
- package/dist/manifest/provider.d.ts.map +1 -0
- package/dist/manifest/provider.js +94 -0
- package/dist/manifest/provider.js.map +1 -0
- package/dist/manifest/scraper.d.ts +11 -0
- package/dist/manifest/scraper.d.ts.map +1 -0
- package/dist/manifest/scraper.js +130 -0
- package/dist/manifest/scraper.js.map +1 -0
- package/dist/output/csv-writer.d.ts +2 -0
- package/dist/output/csv-writer.d.ts.map +1 -0
- package/dist/output/csv-writer.js +94 -0
- package/dist/output/csv-writer.js.map +1 -0
- package/dist/output/json-writer.d.ts +2 -0
- package/dist/output/json-writer.d.ts.map +1 -0
- package/dist/output/json-writer.js +29 -0
- package/dist/output/json-writer.js.map +1 -0
- package/dist/output/names.d.ts +4 -0
- package/dist/output/names.d.ts.map +1 -0
- package/dist/output/names.js +22 -0
- package/dist/output/names.js.map +1 -0
- package/dist/output/writer.d.ts +7 -0
- package/dist/output/writer.d.ts.map +1 -0
- package/dist/output/writer.js +33 -0
- package/dist/output/writer.js.map +1 -0
- package/dist/pool/account-pool.d.ts +29 -0
- package/dist/pool/account-pool.d.ts.map +1 -0
- package/dist/pool/account-pool.js +145 -0
- package/dist/pool/account-pool.js.map +1 -0
- package/dist/pool/cooldown.d.ts +18 -0
- package/dist/pool/cooldown.d.ts.map +1 -0
- package/dist/pool/cooldown.js +38 -0
- package/dist/pool/cooldown.js.map +1 -0
- package/dist/pool/limiter.d.ts +11 -0
- package/dist/pool/limiter.d.ts.map +1 -0
- package/dist/pool/limiter.js +27 -0
- package/dist/pool/limiter.js.map +1 -0
- package/dist/query/builder.d.ts +19 -0
- package/dist/query/builder.d.ts.map +1 -0
- package/dist/query/builder.js +110 -0
- package/dist/query/builder.js.map +1 -0
- package/dist/query/hash.d.ts +2 -0
- package/dist/query/hash.d.ts.map +1 -0
- package/dist/query/hash.js +6 -0
- package/dist/query/hash.js.map +1 -0
- package/dist/query/normalize.d.ts +39 -0
- package/dist/query/normalize.d.ts.map +1 -0
- package/dist/query/normalize.js +176 -0
- package/dist/query/normalize.js.map +1 -0
- package/dist/query/targets.d.ts +11 -0
- package/dist/query/targets.d.ts.map +1 -0
- package/dist/query/targets.js +96 -0
- package/dist/query/targets.js.map +1 -0
- package/dist/runner/index.d.ts +4 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +4 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/retry.d.ts +7 -0
- package/dist/runner/retry.d.ts.map +1 -0
- package/dist/runner/retry.js +26 -0
- package/dist/runner/retry.js.map +1 -0
- package/dist/runner/runner.d.ts +20 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +37 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/runner/task-queue.d.ts +19 -0
- package/dist/runner/task-queue.d.ts.map +1 -0
- package/dist/runner/task-queue.js +58 -0
- package/dist/runner/task-queue.js.map +1 -0
- package/dist/storage/account-repository.d.ts +50 -0
- package/dist/storage/account-repository.d.ts.map +1 -0
- package/dist/storage/account-repository.js +171 -0
- package/dist/storage/account-repository.js.map +1 -0
- package/dist/storage/account-row.d.ts +4 -0
- package/dist/storage/account-row.d.ts.map +1 -0
- package/dist/storage/account-row.js +46 -0
- package/dist/storage/account-row.js.map +1 -0
- package/dist/storage/checkpoint-repository.d.ts +10 -0
- package/dist/storage/checkpoint-repository.d.ts.map +1 -0
- package/dist/storage/checkpoint-repository.js +30 -0
- package/dist/storage/checkpoint-repository.js.map +1 -0
- package/dist/storage/database.d.ts +18 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +42 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/index.d.ts +15 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +18 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/manifest-repository.d.ts +9 -0
- package/dist/storage/manifest-repository.d.ts.map +1 -0
- package/dist/storage/manifest-repository.js +22 -0
- package/dist/storage/manifest-repository.js.map +1 -0
- package/dist/storage/run-repository.d.ts +24 -0
- package/dist/storage/run-repository.d.ts.map +1 -0
- package/dist/storage/run-repository.js +51 -0
- package/dist/storage/run-repository.js.map +1 -0
- package/dist/storage/schema.d.ts +3 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +53 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/transport/graphql.d.ts +8 -0
- package/dist/transport/graphql.d.ts.map +1 -0
- package/dist/transport/graphql.js +104 -0
- package/dist/transport/graphql.js.map +1 -0
- package/dist/transport/proxy.d.ts +5 -0
- package/dist/transport/proxy.d.ts.map +1 -0
- package/dist/transport/proxy.js +36 -0
- package/dist/transport/proxy.js.map +1 -0
- package/dist/transport/session.d.ts +25 -0
- package/dist/transport/session.d.ts.map +1 -0
- package/dist/transport/session.js +138 -0
- package/dist/transport/session.js.map +1 -0
- package/dist/transport/transaction-id.d.ts +20 -0
- package/dist/transport/transaction-id.d.ts.map +1 -0
- package/dist/transport/transaction-id.js +74 -0
- package/dist/transport/transaction-id.js.map +1 -0
- package/dist/utils/fingerprint.d.ts +3 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +20 -0
- package/dist/utils/fingerprint.js.map +1 -0
- package/dist/utils/guards.d.ts +8 -0
- package/dist/utils/guards.d.ts.map +1 -0
- package/dist/utils/guards.js +38 -0
- package/dist/utils/guards.js.map +1 -0
- package/docs/architecture/overview.md +59 -0
- package/docs/decisions/0001-runtime-and-state.md +21 -0
- package/docs/decisions/0002-modular-boundaries.md +16 -0
- package/docs/plans/initial-build.md +18 -0
- package/docs/product/specification.md +87 -0
- package/docs/security/data-boundary.md +27 -0
- package/package.json +89 -0
package/DOCUMENTATION.md
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
1
|
+
# XTrawl documentation
|
|
2
|
+
|
|
3
|
+
This guide explains how to install, authenticate, configure, and operate XTrawl from TypeScript or
|
|
4
|
+
the command line. XTrawl is an authenticated, read-only collector for public X data.
|
|
5
|
+
|
|
6
|
+
## Contents
|
|
7
|
+
|
|
8
|
+
- [Install](#install)
|
|
9
|
+
- [Authenticate](#authenticate)
|
|
10
|
+
- [Use multiple accounts](#use-multiple-accounts)
|
|
11
|
+
- [Use a proxy](#use-a-proxy)
|
|
12
|
+
- [Use the TypeScript API](#use-the-typescript-api)
|
|
13
|
+
- [Search filters](#search-filters)
|
|
14
|
+
- [Use the CLI](#use-the-cli)
|
|
15
|
+
- [Control pagination and resume runs](#control-pagination-and-resume-runs)
|
|
16
|
+
- [Save output](#save-output)
|
|
17
|
+
- [Configure XTrawl](#configure-xtrawl)
|
|
18
|
+
- [Understand return types](#understand-return-types)
|
|
19
|
+
- [Handle errors](#handle-errors)
|
|
20
|
+
- [Refresh operation identifiers](#refresh-operation-identifiers)
|
|
21
|
+
- [Understand storage and account health](#understand-storage-and-account-health)
|
|
22
|
+
- [Manage local state](#manage-local-state)
|
|
23
|
+
- [Protect credentials and collected data](#protect-credentials-and-collected-data)
|
|
24
|
+
- [Troubleshoot common problems](#troubleshoot-common-problems)
|
|
25
|
+
- [Know the limitations](#know-the-limitations)
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
Requirements:
|
|
30
|
+
|
|
31
|
+
- Node.js 22.5 or newer
|
|
32
|
+
- npm
|
|
33
|
+
- An X browser session that you own or are authorized to use
|
|
34
|
+
|
|
35
|
+
Install the package:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install xtrawl
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
To contribute or run the source checkout:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git clone https://github.com/ensp1re/xtrawl.git
|
|
45
|
+
cd xtrawl
|
|
46
|
+
npm install
|
|
47
|
+
npm run build
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The source build writes ESM output to `dist/`. In source-checkout commands, replace `xtrawl` with
|
|
51
|
+
`npm run cli --`.
|
|
52
|
+
|
|
53
|
+
Verify the checkout:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run check
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Authenticate
|
|
60
|
+
|
|
61
|
+
Live requests require the `auth_token` and `ct0` cookies from an authenticated X browser session.
|
|
62
|
+
XTrawl calls these values `authToken` and `csrfToken` in TypeScript and reads them from
|
|
63
|
+
`X_AUTH_TOKEN` and `X_CSRF_TOKEN` in the process environment.
|
|
64
|
+
|
|
65
|
+
### Use environment variables
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export X_AUTH_TOKEN="your-auth-token"
|
|
69
|
+
export X_CSRF_TOKEN="your-ct0-token"
|
|
70
|
+
|
|
71
|
+
xtrawl user-info OpenAI --pretty
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This is the smallest setup for a single account.
|
|
75
|
+
|
|
76
|
+
### Use an environment file
|
|
77
|
+
|
|
78
|
+
Create a local file that is not committed, for example `.env.local`:
|
|
79
|
+
|
|
80
|
+
```dotenv
|
|
81
|
+
X_AUTH_TOKEN=your-auth-token
|
|
82
|
+
X_CSRF_TOKEN=your-ct0-token
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Pass global options before the command:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
xtrawl --env-file .env.local user-info OpenAI --pretty
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Environment files also recognize `AUTH_TOKEN` for the auth cookie and `CT0` or `CSRF` for the CSRF
|
|
92
|
+
cookie. Prefer the `X_AUTH_TOKEN` and `X_CSRF_TOKEN` names for clarity.
|
|
93
|
+
|
|
94
|
+
### Bootstrap a missing CSRF cookie
|
|
95
|
+
|
|
96
|
+
`XTrawl.create()` checks provisioned accounts that have an auth token but no CSRF token. It attempts
|
|
97
|
+
to obtain the missing `ct0` cookie before returning the client:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const client = await XTrawl.create({
|
|
101
|
+
authToken: process.env.X_AUTH_TOKEN,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Supplying both cookies is more deterministic. A session without usable authentication is not
|
|
106
|
+
eligible for live requests.
|
|
107
|
+
|
|
108
|
+
## Use multiple accounts
|
|
109
|
+
|
|
110
|
+
XTrawl can load account records from inline objects or a file and keep account health in SQLite.
|
|
111
|
+
For JSON files, use one object or an array of objects:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
[
|
|
115
|
+
{
|
|
116
|
+
"username": "collector-one",
|
|
117
|
+
"authToken": "replace-me",
|
|
118
|
+
"csrfToken": "replace-me"
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"username": "collector-two",
|
|
122
|
+
"cookies": {
|
|
123
|
+
"auth_token": "replace-me",
|
|
124
|
+
"ct0": "replace-me"
|
|
125
|
+
},
|
|
126
|
+
"proxy": "http://127.0.0.1:8080"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Never commit this file. Load it from the CLI:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
xtrawl --cookies-file ./accounts.json search "typescript" --limit 100
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Or from TypeScript:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const client = await XTrawl.create({
|
|
141
|
+
accountsFile: "./accounts.json",
|
|
142
|
+
dbPath: "./state/xtrawl.db",
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Supported account sources are:
|
|
147
|
+
|
|
148
|
+
| Source | CLI or library option | Notes |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| Process environment | No option required | Reads `X_AUTH_TOKEN` and `X_CSRF_TOKEN` |
|
|
151
|
+
| Environment file | `--env-file` / `envFile` | Reads dotenv-style values |
|
|
152
|
+
| JSON account file | `--cookies-file` / `cookiesFile` or `accountsFile` | Accepts one account or an array |
|
|
153
|
+
| Netscape cookie file | `--cookies-file` / `cookiesFile` | Extracts browser cookie records |
|
|
154
|
+
| Delimited account file | `--cookies-file` / `cookiesFile` | Accepts pipe- or colon-separated records |
|
|
155
|
+
| Inline account records | `accounts` | TypeScript API only |
|
|
156
|
+
| Inline cookie payload | `cookies` | TypeScript API only; accepts an object or cookie string |
|
|
157
|
+
|
|
158
|
+
Delimited files are accepted for account import compatibility, but live sessions still require
|
|
159
|
+
usable `auth_token` and `ct0` values. XTrawl does not perform interactive username/password login.
|
|
160
|
+
|
|
161
|
+
If you want to reuse accounts already stored in the configured SQLite database without provisioning
|
|
162
|
+
new input, create the client with `provision: false`.
|
|
163
|
+
|
|
164
|
+
## Use a proxy
|
|
165
|
+
|
|
166
|
+
Set one proxy for all accounts:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
xtrawl --proxy http://127.0.0.1:8080 search "typescript" --limit 20
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The TypeScript API accepts a URL or structured proxy settings:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const client = await XTrawl.create({
|
|
176
|
+
authToken: process.env.X_AUTH_TOKEN,
|
|
177
|
+
csrfToken: process.env.X_CSRF_TOKEN,
|
|
178
|
+
proxy: {
|
|
179
|
+
scheme: "http",
|
|
180
|
+
host: "127.0.0.1",
|
|
181
|
+
port: 8080,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
An account record may define its own `proxy`; that account-level value takes precedence for its
|
|
187
|
+
session. HTTP, HTTPS, and SOCKS5 URLs are supported. Before leasing a proxied account, XTrawl performs
|
|
188
|
+
a short unauthenticated health request through that proxy and caches a successful result for one
|
|
189
|
+
minute. Keep proxy credentials in secret storage, not in committed configuration.
|
|
190
|
+
|
|
191
|
+
## Use the TypeScript API
|
|
192
|
+
|
|
193
|
+
The package is ESM:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import {
|
|
197
|
+
AuthError,
|
|
198
|
+
RateLimitError,
|
|
199
|
+
XTrawl,
|
|
200
|
+
type SearchResult,
|
|
201
|
+
} from "xtrawl";
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Create and close a client
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
const client = await XTrawl.create({
|
|
208
|
+
authToken: process.env.X_AUTH_TOKEN,
|
|
209
|
+
csrfToken: process.env.X_CSRF_TOKEN,
|
|
210
|
+
dbPath: "xtrawl.db",
|
|
211
|
+
saveDir: "outputs",
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Run operations here.
|
|
216
|
+
} finally {
|
|
217
|
+
client.close();
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Always close the client when the process no longer needs it so the SQLite connection is released.
|
|
222
|
+
|
|
223
|
+
The synchronous constructor is also public:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const client = new XTrawl({
|
|
227
|
+
authToken: process.env.X_AUTH_TOKEN,
|
|
228
|
+
csrfToken: process.env.X_CSRF_TOKEN,
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Prefer `XTrawl.create()` because it can bootstrap a missing CSRF cookie.
|
|
233
|
+
|
|
234
|
+
### Search posts
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const result: SearchResult = await client.search("typescript", {
|
|
238
|
+
since: "2026-01-01",
|
|
239
|
+
until: "2026-02-01",
|
|
240
|
+
fromUsers: ["OpenAI", "github"],
|
|
241
|
+
exactPhrases: ["open source"],
|
|
242
|
+
excludeWords: ["hiring"],
|
|
243
|
+
lang: "en",
|
|
244
|
+
minLikes: 10,
|
|
245
|
+
hasLinks: true,
|
|
246
|
+
tweetType: "originals_only",
|
|
247
|
+
displayType: "Latest",
|
|
248
|
+
limit: 200,
|
|
249
|
+
resume: true,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
console.log(result.tweets);
|
|
253
|
+
console.log(result.stats);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The first argument is the free-form search query. Typed options are normalized into X search
|
|
257
|
+
operators and combined with that query. If neither date bound is supplied, XTrawl searches the
|
|
258
|
+
previous 30 days. A bounded interval is split into up to `searchSplits` tasks and processed with the
|
|
259
|
+
available account concurrency.
|
|
260
|
+
|
|
261
|
+
### Read profile information
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
const profiles = await client.getUserInfo([
|
|
265
|
+
"OpenAI",
|
|
266
|
+
"@github",
|
|
267
|
+
{ profileUrl: "https://x.com/typescript" },
|
|
268
|
+
]);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Each resolvable public target returns a normalized `ProfileRecord`.
|
|
272
|
+
|
|
273
|
+
Profile information can also be saved:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
await client.getUserInfo(["OpenAI", "github"], {
|
|
277
|
+
save: true,
|
|
278
|
+
saveFormat: "both",
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Read one post
|
|
283
|
+
|
|
284
|
+
Pass a numeric post ID or an X status URL:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
const tweet = await client.getTweet("https://x.com/OpenAI/status/1234567890");
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The method returns a normalized `TweetRecord`, or `undefined` when the response has no post result.
|
|
291
|
+
|
|
292
|
+
### Collect profile posts
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const result = await client.getProfileTweets(["OpenAI", "github"], {
|
|
296
|
+
limit: 500,
|
|
297
|
+
perProfileLimit: 200,
|
|
298
|
+
maxPagesPerProfile: 20,
|
|
299
|
+
maxEmptyPages: 2,
|
|
300
|
+
resume: true,
|
|
301
|
+
save: true,
|
|
302
|
+
saveFormat: "both",
|
|
303
|
+
saveName: "profile-posts",
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`limit` caps all returned posts. `perProfileLimit` caps each target independently.
|
|
308
|
+
|
|
309
|
+
### Collect followers and following
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
const followers = await client.getFollowers(["OpenAI"], {
|
|
313
|
+
limit: 500,
|
|
314
|
+
resume: true,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const following = await client.getFollowing(["OpenAI"], {
|
|
318
|
+
perProfileLimit: 250,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const verifiedFollowers = await client.getVerifiedFollowers(["OpenAI"], {
|
|
322
|
+
maxPagesPerProfile: 10,
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
These methods return normalized `FollowRecord` arrays. Records are deduplicated within each target.
|
|
327
|
+
|
|
328
|
+
### Inspect local state
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
const inspection = client.inspect();
|
|
332
|
+
|
|
333
|
+
console.log(inspection.config);
|
|
334
|
+
console.log(inspection.accounts);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
`inspect()` reports the validated configuration and redacted account projections. Tokens, cookie
|
|
338
|
+
values, passwords, email addresses, bearer overrides, and proxy credentials are not returned.
|
|
339
|
+
|
|
340
|
+
## Search filters
|
|
341
|
+
|
|
342
|
+
`SearchRequest` supports these canonical options:
|
|
343
|
+
|
|
344
|
+
| Option | Type | Effect |
|
|
345
|
+
| --- | --- | --- |
|
|
346
|
+
| `searchQuery` | `string` | Free-form query; the first `search()` argument overrides it when non-empty |
|
|
347
|
+
| `since`, `until` | `string` | Lower and upper date bounds |
|
|
348
|
+
| `allWords` | `string[]` | Require every supplied term |
|
|
349
|
+
| `anyWords` | `string[]` | Require at least one supplied term |
|
|
350
|
+
| `exactPhrases` | `string[]` | Match quoted phrases |
|
|
351
|
+
| `excludeWords` | `string[]` | Exclude supplied terms |
|
|
352
|
+
| `hashtagsAny` | `string[]` | Match any supplied hashtag |
|
|
353
|
+
| `hashtagsExclude` | `string[]` | Exclude supplied hashtags |
|
|
354
|
+
| `fromUsers` | `string[]` | Match posts from these accounts |
|
|
355
|
+
| `toUsers` | `string[]` | Match posts addressed to these accounts |
|
|
356
|
+
| `mentioningUsers` | `string[]` | Match posts mentioning these accounts |
|
|
357
|
+
| `tweetType` | `TweetType` | Select or exclude originals, replies, or reposts |
|
|
358
|
+
| `verifiedOnly` | `boolean` | Require verified authors |
|
|
359
|
+
| `blueVerifiedOnly` | `boolean` | Require blue-verified authors |
|
|
360
|
+
| `hasImages` | `boolean` | Require images |
|
|
361
|
+
| `hasVideos` | `boolean` | Require videos |
|
|
362
|
+
| `hasLinks` | `boolean` | Require links |
|
|
363
|
+
| `hasMentions` | `boolean` | Require mentions |
|
|
364
|
+
| `hasHashtags` | `boolean` | Require hashtags |
|
|
365
|
+
| `minLikes` | `number` | Minimum like count |
|
|
366
|
+
| `minReplies` | `number` | Minimum reply count |
|
|
367
|
+
| `minRetweets` | `number` | Minimum repost count |
|
|
368
|
+
| `place` | `string` | Match an X place operator |
|
|
369
|
+
| `geocode` | `string` | Match a geocode expression |
|
|
370
|
+
| `near`, `within` | `string` | Match a named location and radius |
|
|
371
|
+
| `lang` | `string` | Match a language code |
|
|
372
|
+
| `displayType` | `"Top" \| "Latest"` | Select the search timeline mode |
|
|
373
|
+
| `limit` | `number` | Cap the number of returned posts |
|
|
374
|
+
| `maxEmptyPages` | `number` | Stop after this many consecutive empty pages |
|
|
375
|
+
|
|
376
|
+
`TweetType` accepts `all`, `originals_only`, `replies_only`, `retweets_only`, `exclude_replies`, or
|
|
377
|
+
`exclude_retweets`.
|
|
378
|
+
|
|
379
|
+
## Use the CLI
|
|
380
|
+
|
|
381
|
+
Syntax:
|
|
382
|
+
|
|
383
|
+
```text
|
|
384
|
+
xtrawl [global options] COMMAND [values] [command options]
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Use `npx xtrawl` if you installed XTrawl locally rather than globally. From a source checkout,
|
|
388
|
+
replace `xtrawl` with `npm run cli --`.
|
|
389
|
+
|
|
390
|
+
### Commands
|
|
391
|
+
|
|
392
|
+
| Command | Values | Result |
|
|
393
|
+
| --- | --- | --- |
|
|
394
|
+
| `search` | Optional query | Matching public posts and run statistics |
|
|
395
|
+
| `tweet` | One or more post IDs or status URLs | Individual public post records |
|
|
396
|
+
| `profile-tweets` | One or more users | Posts from public profile timelines |
|
|
397
|
+
| `followers` | One or more users | Public follower relationships |
|
|
398
|
+
| `following` | One or more users | Public following relationships |
|
|
399
|
+
| `verified-followers` | One or more users | Public verified-follower relationships |
|
|
400
|
+
| `user-info` | One or more users | Public profile records |
|
|
401
|
+
|
|
402
|
+
### Global options
|
|
403
|
+
|
|
404
|
+
Put these before the command:
|
|
405
|
+
|
|
406
|
+
| Option | Value | Purpose |
|
|
407
|
+
| --- | --- | --- |
|
|
408
|
+
| `--auth-token` | token | Supply one account's `auth_token` value |
|
|
409
|
+
| `--csrf-token`, `--ct0` | token | Supply one account's `ct0` value |
|
|
410
|
+
| `--cookies-file` | path | Load accounts or cookies from a file |
|
|
411
|
+
| `--env-file` | path | Load account values from a dotenv file |
|
|
412
|
+
| `--db-path` | path | Choose the SQLite state file |
|
|
413
|
+
| `--proxy` | URL | Set the default HTTP(S) or SOCKS5 proxy |
|
|
414
|
+
| `--concurrency` | positive integer | Set the configured worker count |
|
|
415
|
+
| `--manifest-scrape-on-init` | flag | Enable live operation-identifier refresh with local fallback |
|
|
416
|
+
| `--verbose`, `-v` | flag | Print redacted pool diagnostics and full error stacks |
|
|
417
|
+
| `--help` | flag | Print CLI help |
|
|
418
|
+
|
|
419
|
+
Example:
|
|
420
|
+
|
|
421
|
+
```bash
|
|
422
|
+
xtrawl \
|
|
423
|
+
--env-file .env.local \
|
|
424
|
+
--db-path ./state/xtrawl.db \
|
|
425
|
+
search "typescript" \
|
|
426
|
+
--limit 100
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Search options
|
|
430
|
+
|
|
431
|
+
| CLI option | API option |
|
|
432
|
+
| --- | --- |
|
|
433
|
+
| `--since`, `--until` | `since`, `until` |
|
|
434
|
+
| `--from`, `--to`, `--mention` | `fromUsers`, `toUsers`, `mentioningUsers` |
|
|
435
|
+
| `--all-words`, `--any-words`, `--exact-phrases` | `allWords`, `anyWords`, `exactPhrases` |
|
|
436
|
+
| `--exclude-words` | `excludeWords` |
|
|
437
|
+
| `--hashtags-any`, `--hashtags-exclude` | `hashtagsAny`, `hashtagsExclude` |
|
|
438
|
+
| `--lang`, `--place`, `--geocode`, `--near`, `--within` | Location and language filters |
|
|
439
|
+
| `--display-type` | `displayType` |
|
|
440
|
+
| `--tweet-type` | `tweetType` |
|
|
441
|
+
| `--verified-only`, `--blue-verified-only` | Verification filters |
|
|
442
|
+
| `--has-images`, `--has-videos`, `--has-links` | Media and link filters |
|
|
443
|
+
| `--has-mentions`, `--has-hashtags` | Entity filters |
|
|
444
|
+
| `--min-likes`, `--min-replies`, `--min-retweets` | Engagement thresholds |
|
|
445
|
+
| `--limit`, `--max-empty-pages` | Pagination stop conditions |
|
|
446
|
+
|
|
447
|
+
Repeat list options or place multiple values after one list option:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
xtrawl search "release" \
|
|
451
|
+
--from OpenAI \
|
|
452
|
+
--from github \
|
|
453
|
+
--exact-phrases "open source" \
|
|
454
|
+
--lang en \
|
|
455
|
+
--limit 100
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Collection options
|
|
459
|
+
|
|
460
|
+
`profile-tweets`, `followers`, `following`, and `verified-followers` accept:
|
|
461
|
+
|
|
462
|
+
| Option | Purpose |
|
|
463
|
+
| --- | --- |
|
|
464
|
+
| `--limit` | Cap all returned records |
|
|
465
|
+
| `--per-profile-limit` | Cap records for each target |
|
|
466
|
+
| `--max-pages-per-profile` | Cap pages for each target |
|
|
467
|
+
| `--max-empty-pages` | Stop a target after consecutive empty pages |
|
|
468
|
+
| `--resume` | Read and update SQLite checkpoints |
|
|
469
|
+
| `--save` | Write output files |
|
|
470
|
+
| `--save-format csv\|json\|both` | Select output formats |
|
|
471
|
+
| `--save-dir` | Select the output directory |
|
|
472
|
+
| `--save-name` | Select the output filename without an extension |
|
|
473
|
+
| `--raw-json` | Include raw user payloads in relationship JSON output |
|
|
474
|
+
|
|
475
|
+
All commands accept `--pretty` after the command to print indented JSON to stdout. Without
|
|
476
|
+
`--pretty`, use `--save` when you need file output.
|
|
477
|
+
|
|
478
|
+
## Control pagination and resume runs
|
|
479
|
+
|
|
480
|
+
XTrawl follows each response cursor until one of these conditions is met:
|
|
481
|
+
|
|
482
|
+
- The requested global or per-profile record limit is reached.
|
|
483
|
+
- The target reaches `maxPagesPerProfile`.
|
|
484
|
+
- The response has no next cursor.
|
|
485
|
+
- Consecutive empty pages reach `maxEmptyPages`.
|
|
486
|
+
- The operation fails.
|
|
487
|
+
|
|
488
|
+
With `resume: true` or `--resume`, XTrawl reads a matching cursor from SQLite before the run and
|
|
489
|
+
updates it as pagination advances. Checkpoint identity includes the operation and normalized request,
|
|
490
|
+
so a materially different request starts from its own checkpoint. A successfully completed operation
|
|
491
|
+
clears its checkpoint.
|
|
492
|
+
|
|
493
|
+
For profile and relationship methods, `initialCursors` can provide an explicit cursor keyed by the
|
|
494
|
+
target identity. An explicit initial cursor takes precedence over a stored checkpoint.
|
|
495
|
+
|
|
496
|
+
## Save output
|
|
497
|
+
|
|
498
|
+
Enable file output per operation:
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
const result = await client.search("typescript", {
|
|
502
|
+
limit: 100,
|
|
503
|
+
save: true,
|
|
504
|
+
saveFormat: "both",
|
|
505
|
+
saveDir: "./exports",
|
|
506
|
+
saveName: "typescript-posts",
|
|
507
|
+
});
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
This writes:
|
|
511
|
+
|
|
512
|
+
```text
|
|
513
|
+
exports/typescript-posts.csv
|
|
514
|
+
exports/typescript-posts.json
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Without overrides, the directory is `outputs`, the format is `csv`, and the base name reflects the
|
|
518
|
+
query/date range or operation targets. Existing CSV and JSON files are appended instead of replaced.
|
|
519
|
+
|
|
520
|
+
JSON preserves the normalized records. Tweet CSV output flattens the common post fields, including
|
|
521
|
+
the post ID, timestamp, author, text, engagement counts, URL, and media links.
|
|
522
|
+
|
|
523
|
+
## Configure XTrawl
|
|
524
|
+
|
|
525
|
+
Pass configuration fields to `XTrawl.create()` or the constructor. Common defaults are:
|
|
526
|
+
|
|
527
|
+
| Option | Default | Purpose |
|
|
528
|
+
| --- | --- | --- |
|
|
529
|
+
| `dbPath` | `xtrawl_state.db` | SQLite account and run state |
|
|
530
|
+
| `concurrency` | `5` | Configured worker count |
|
|
531
|
+
| `saveDir` | `outputs` | File output directory |
|
|
532
|
+
| `saveFormat` | `csv` | File output format |
|
|
533
|
+
| `apiPageSize` | `20` | Requested records per API page |
|
|
534
|
+
| `searchSplits` | `5` | Maximum date intervals per search |
|
|
535
|
+
| `schedulerMinIntervalMs` | `300000` | Smallest search interval |
|
|
536
|
+
| `maxEmptyPages` | `1` | Consecutive empty-page stop threshold |
|
|
537
|
+
| `dailyRequestsLimit` | `30` | Per-account daily operation guard |
|
|
538
|
+
| `dailyTweetsLimit` | `600` | Per-account daily collected-post guard |
|
|
539
|
+
| `cooldownDefaultMs` | `120000` | Default rate-limit cooldown |
|
|
540
|
+
| `transientCooldownMs` | `120000` | Network and transient cooldown |
|
|
541
|
+
| `leaseTtlMs` | `120000` | Account lease lifetime |
|
|
542
|
+
| `leaseHeartbeatMs` | `30000` | Active lease renewal interval |
|
|
543
|
+
| `requestsPerMinute` | `30` | Per-account token-bucket rate |
|
|
544
|
+
| `minDelayMs` | `2000` | Minimum spacing between account requests |
|
|
545
|
+
| `maxTaskAttempts` | `3` | Attempts for a failed page request |
|
|
546
|
+
| `maxAccountSwitches` | `2` | Account changes allowed within one page request |
|
|
547
|
+
| `proxyCheckOnLease` | `true` | Check a configured proxy before use |
|
|
548
|
+
| `proxyCheckTimeoutMs` | `10000` | Proxy health-check timeout |
|
|
549
|
+
| `manifestTtlMs` | `3600000` | Cached remote manifest lifetime |
|
|
550
|
+
| `manifestUpdateOnInit` | `false` | Force configured manifest URL refresh on first use |
|
|
551
|
+
| `manifestScrapeOnInit` | `false` | Enable live operation-identifier refresh |
|
|
552
|
+
| `transactionIdEnabled` | `true` | Generate current web transaction headers when possible |
|
|
553
|
+
| `transactionIdTtlMs` | `21600000` | Reuse transaction bootstrap material for six hours |
|
|
554
|
+
| `strict` | `false` | Fail a multi-target run when any task fails |
|
|
555
|
+
|
|
556
|
+
Limits are local safeguards, not statements about the platform's actual limits. XTrawl validates
|
|
557
|
+
configuration before opening a live operation; positive fields must be valid positive numbers and
|
|
558
|
+
`apiPageSize` cannot exceed 100.
|
|
559
|
+
|
|
560
|
+
## Understand return types
|
|
561
|
+
|
|
562
|
+
### `SearchResult`
|
|
563
|
+
|
|
564
|
+
Search and profile timeline methods return:
|
|
565
|
+
|
|
566
|
+
```ts
|
|
567
|
+
interface SearchResult {
|
|
568
|
+
readonly tweets: readonly TweetRecord[];
|
|
569
|
+
readonly stats: RunStats;
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
`TweetRecord` includes the post ID, author, timestamp, text, engagement counts, image links, post URL,
|
|
574
|
+
and optional raw source data when available. `RunStats` reports collected count, task counts,
|
|
575
|
+
failures, and retries.
|
|
576
|
+
|
|
577
|
+
### `ProfileRecord`
|
|
578
|
+
|
|
579
|
+
Profile lookup returns normalized identity, biography, location, account creation time, public
|
|
580
|
+
counts, verification flags, protection status, profile images, banner, URL, and optional raw data.
|
|
581
|
+
|
|
582
|
+
### `FollowRecord`
|
|
583
|
+
|
|
584
|
+
Relationship methods return profile fields plus the relationship type and the original target. The
|
|
585
|
+
type is `followers`, `following`, or `verified_followers`.
|
|
586
|
+
|
|
587
|
+
All response shapes are exported from the package entry point as TypeScript types.
|
|
588
|
+
|
|
589
|
+
## Handle errors
|
|
590
|
+
|
|
591
|
+
Public failures extend `XTrawlError` and expose a `code` plus structured diagnostics:
|
|
592
|
+
|
|
593
|
+
```ts
|
|
594
|
+
import {
|
|
595
|
+
AuthError,
|
|
596
|
+
RateLimitError,
|
|
597
|
+
XTrawlError,
|
|
598
|
+
} from "xtrawl";
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
await client.search("typescript", { limit: 100 });
|
|
602
|
+
} catch (error) {
|
|
603
|
+
if (error instanceof RateLimitError) {
|
|
604
|
+
console.error("The active account was rate limited.");
|
|
605
|
+
} else if (error instanceof AuthError) {
|
|
606
|
+
console.error("Refresh the account cookies.");
|
|
607
|
+
} else if (error instanceof XTrawlError) {
|
|
608
|
+
console.error(error.code, error.message, error.diagnostics);
|
|
609
|
+
} else {
|
|
610
|
+
throw error;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Exported error classes include:
|
|
616
|
+
|
|
617
|
+
- `ConfigError` for invalid configuration
|
|
618
|
+
- `ManifestError` for invalid or unavailable operation manifests
|
|
619
|
+
- `AccountPoolExhausted` when no account is eligible
|
|
620
|
+
- `AuthError` for rejected authentication
|
|
621
|
+
- `RateLimitError` for platform rate limits
|
|
622
|
+
- `NetworkError` and `ProxyError` for transport failures
|
|
623
|
+
- `EngineError` and `RunFailed` for operation-level failures
|
|
624
|
+
- `ResumeError` for invalid checkpoint state
|
|
625
|
+
|
|
626
|
+
Do not add raw tokens or cookie values to application logs when handling an error.
|
|
627
|
+
|
|
628
|
+
## Refresh operation identifiers
|
|
629
|
+
|
|
630
|
+
X's web operation identifiers can change. XTrawl automatically refreshes the authenticated web
|
|
631
|
+
manifest and retries once when X rejects an outdated operation ID with HTTP 404 or 422. You can also
|
|
632
|
+
refresh before the first operation:
|
|
633
|
+
|
|
634
|
+
```bash
|
|
635
|
+
xtrawl --manifest-scrape-on-init search "typescript" --limit 20
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Or in TypeScript:
|
|
639
|
+
|
|
640
|
+
```ts
|
|
641
|
+
const client = await XTrawl.create({
|
|
642
|
+
authToken: process.env.X_AUTH_TOKEN,
|
|
643
|
+
csrfToken: process.env.X_CSRF_TOKEN,
|
|
644
|
+
manifestScrapeOnInit: true,
|
|
645
|
+
});
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
Refresh reads the authenticated responsive-web main bundle, accepts only supported X script hosts,
|
|
649
|
+
and requires at least one real operation match. If an optional startup refresh fails, XTrawl falls
|
|
650
|
+
back to the bundled manifest. A configured `manifestUrl` can also provide a remote JSON manifest;
|
|
651
|
+
XTrawl caches it in SQLite and can use a stale cached value when a refresh fails.
|
|
652
|
+
|
|
653
|
+
## Understand storage and account health
|
|
654
|
+
|
|
655
|
+
XTrawl uses SQLite for operational state:
|
|
656
|
+
|
|
657
|
+
- Provisioned accounts and their health status
|
|
658
|
+
- Exclusive account leases and lease expiry
|
|
659
|
+
- Daily request and collected-post counters
|
|
660
|
+
- Cooldown and last-error information
|
|
661
|
+
- Run status and failure summaries
|
|
662
|
+
- Pagination checkpoints
|
|
663
|
+
- Cached operation manifests
|
|
664
|
+
|
|
665
|
+
Before a request, the account pool selects an account that has authentication material, is not
|
|
666
|
+
leased, is not cooling down, and remains within configured local limits. After the operation:
|
|
667
|
+
|
|
668
|
+
- A successful account returns to the eligible pool.
|
|
669
|
+
- A rate-limit, network, proxy, or transient failure applies the corresponding cooldown.
|
|
670
|
+
- An authentication rejection marks the account unusable so it is not selected again.
|
|
671
|
+
|
|
672
|
+
SQLite coordinates account leases so separate work does not intentionally use the same stored
|
|
673
|
+
account at the same time.
|
|
674
|
+
|
|
675
|
+
## Manage local state
|
|
676
|
+
|
|
677
|
+
`client.db` provides scoped operational maintenance without exposing the storage implementation:
|
|
678
|
+
|
|
679
|
+
```ts
|
|
680
|
+
console.log(client.db.accountsSummary());
|
|
681
|
+
console.log(client.db.listAccounts({ eligibleOnly: true }));
|
|
682
|
+
|
|
683
|
+
client.db.setAccountProxy("collector-one", "socks5://127.0.0.1:1080");
|
|
684
|
+
await client.db.repairAccount("collector-one", true);
|
|
685
|
+
client.db.resetAccountCooldowns(["collector-one"], true);
|
|
686
|
+
client.db.clearLeases(true);
|
|
687
|
+
client.db.resetDailyCounters();
|
|
688
|
+
|
|
689
|
+
console.log(client.db.lastRun());
|
|
690
|
+
console.log(client.db.runsSummary());
|
|
691
|
+
client.db.clearAllCheckpoints();
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
Account listings are redacted by default. `importAccounts()` accepts the same inline and file inputs
|
|
695
|
+
as client provisioning. `deleteAccount(username)` deletes only the named row.
|
|
696
|
+
`collapseDuplicateAccounts()` reports what it would remove; pass `false` only when you explicitly
|
|
697
|
+
want to merge and delete duplicate token rows.
|
|
698
|
+
|
|
699
|
+
## Protect credentials and collected data
|
|
700
|
+
|
|
701
|
+
Treat the following as sensitive:
|
|
702
|
+
|
|
703
|
+
- `auth_token`, `ct0`, bearer overrides, and complete cookie jars
|
|
704
|
+
- SQLite state files containing provisioned account records
|
|
705
|
+
- Proxy URLs containing usernames or passwords
|
|
706
|
+
- Collected output that may contain personal data
|
|
707
|
+
|
|
708
|
+
Follow these rules:
|
|
709
|
+
|
|
710
|
+
1. Keep secrets in environment variables or a caller-owned secret store.
|
|
711
|
+
2. Never commit `.env` files, account files, SQLite databases, or real response fixtures.
|
|
712
|
+
3. Use dedicated accounts that you own or are explicitly authorized to operate.
|
|
713
|
+
4. Restrict filesystem permissions and retention for state and output files.
|
|
714
|
+
5. Keep even redacted diagnostics and `inspect()` output within trusted operational tooling.
|
|
715
|
+
6. Collect only what you need and follow platform terms and applicable law.
|
|
716
|
+
|
|
717
|
+
XTrawl sends read-only HTTP requests. Its transport does not implement posting, replying, liking,
|
|
718
|
+
following, messaging, or account-administration operations.
|
|
719
|
+
|
|
720
|
+
## Troubleshoot common problems
|
|
721
|
+
|
|
722
|
+
### No eligible account is available
|
|
723
|
+
|
|
724
|
+
Check that at least one account has both usable `auth_token` and `ct0` values. The account may also be
|
|
725
|
+
leased, cooling down, marked unusable, or over a configured daily guard. Use `client.inspect()` in a
|
|
726
|
+
trusted local process to review account status.
|
|
727
|
+
|
|
728
|
+
### Authentication fails
|
|
729
|
+
|
|
730
|
+
Refresh both browser session cookies and use a fresh state database or deliberately reset the stored
|
|
731
|
+
account state before provisioning them again. Confirm they came from the same authorized session.
|
|
732
|
+
Authentication failures mark the stored account unusable.
|
|
733
|
+
|
|
734
|
+
### Search returns no posts
|
|
735
|
+
|
|
736
|
+
Start with a simpler query, remove restrictive filters, and use `displayType: "Latest"`. An empty page
|
|
737
|
+
can also mean the active account cannot view the requested content.
|
|
738
|
+
|
|
739
|
+
### A run stops early
|
|
740
|
+
|
|
741
|
+
Review `limit`, `perProfileLimit`, `maxPagesPerProfile`, and `maxEmptyPages`. The endpoint may also
|
|
742
|
+
have returned no next cursor. Enable `resume` before long runs so interrupted pagination can continue.
|
|
743
|
+
|
|
744
|
+
### An endpoint suddenly fails
|
|
745
|
+
|
|
746
|
+
XTrawl retries once with an authenticated manifest refresh after HTTP 404 or 422. If the retry also
|
|
747
|
+
fails, the endpoint path, variables, or response shape may have changed and code changes may be
|
|
748
|
+
required.
|
|
749
|
+
|
|
750
|
+
### The CLI treats a global option as a command option
|
|
751
|
+
|
|
752
|
+
Move global options before the command:
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
# Correct
|
|
756
|
+
xtrawl --db-path ./state/xtrawl.db search "typescript" --limit 20
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
## Know the limitations
|
|
760
|
+
|
|
761
|
+
- XTrawl collects only content visible to the authenticated session. It does not bypass protected or
|
|
762
|
+
private accounts.
|
|
763
|
+
- The project depends on undocumented X web GraphQL endpoints, response shapes, and query IDs. They
|
|
764
|
+
can change without notice.
|
|
765
|
+
- Browser cookies expire and may be invalidated by account security events.
|
|
766
|
+
- Local cooldowns, delays, and usage guards cannot guarantee account availability or uninterrupted
|
|
767
|
+
collection.
|
|
768
|
+
- Pagination completeness depends on the cursors and records returned by the platform.
|
|
769
|
+
- Live integration tests require caller-supplied credentials and do not run in the default test gate.
|
|
770
|
+
|
|
771
|
+
For the behavioral contract, architecture boundaries, and security decisions, read the
|
|
772
|
+
[product specification](docs/product/specification.md),
|
|
773
|
+
[architecture overview](docs/architecture/overview.md), and
|
|
774
|
+
[security and data boundary](docs/security/data-boundary.md).
|