scraper-api-com 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.
Files changed (2) hide show
  1. package/dist/scraper-api.js +3658 -0
  2. package/package.json +60 -0
@@ -0,0 +1,3658 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.js
4
+ import { Command as Command2 } from "commander";
5
+ import chalk9 from "chalk";
6
+
7
+ // src/command-registry.js
8
+ import { Command } from "commander";
9
+ import chalk from "chalk";
10
+ import ora from "ora";
11
+
12
+ // ../../packages/api-client/src/endpoints.generated.js
13
+ var endpoints = [
14
+ {
15
+ "platform": "instagram",
16
+ "action": "profile",
17
+ "resource": "profile",
18
+ "version": "v1",
19
+ "method": "GET",
20
+ "path": "/v1/instagram/profile",
21
+ "toolName": "v1_instagram_profile",
22
+ "summary": "Get an Instagram user's full profile by handle (followers/bio need a backend session).",
23
+ "cost": 1,
24
+ "live": true,
25
+ "anyOf": [
26
+ [
27
+ "handle"
28
+ ]
29
+ ],
30
+ "params": [
31
+ {
32
+ "name": "handle",
33
+ "type": "string",
34
+ "required": false,
35
+ "anyOf": true
36
+ }
37
+ ]
38
+ },
39
+ {
40
+ "platform": "instagram",
41
+ "action": "basic-profile",
42
+ "resource": "basic-profile",
43
+ "version": "v1",
44
+ "method": "GET",
45
+ "path": "/v1/instagram/basic-profile",
46
+ "toolName": "v1_instagram_basic_profile",
47
+ "summary": "Get an Instagram user's basic public profile (name, verified, private, picture) by handle.",
48
+ "cost": 1,
49
+ "live": true,
50
+ "anyOf": [
51
+ [
52
+ "handle"
53
+ ]
54
+ ],
55
+ "params": [
56
+ {
57
+ "name": "handle",
58
+ "type": "string",
59
+ "required": false,
60
+ "anyOf": true
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "platform": "instagram",
66
+ "action": "user-posts",
67
+ "resource": "user/posts",
68
+ "version": "v2",
69
+ "method": "GET",
70
+ "path": "/v2/instagram/user/posts",
71
+ "toolName": "v2_instagram_user_posts",
72
+ "summary": "Get an Instagram user's posts (incl. reels) by handle; paginate via next_max_id.",
73
+ "cost": 1,
74
+ "live": true,
75
+ "anyOf": [
76
+ [
77
+ "handle"
78
+ ]
79
+ ],
80
+ "params": [
81
+ {
82
+ "name": "handle",
83
+ "type": "string",
84
+ "required": false,
85
+ "anyOf": true
86
+ }
87
+ ]
88
+ },
89
+ {
90
+ "platform": "instagram",
91
+ "action": "post",
92
+ "resource": "post",
93
+ "version": "v1",
94
+ "method": "GET",
95
+ "path": "/v1/instagram/post",
96
+ "toolName": "v1_instagram_post",
97
+ "summary": "Get a single Instagram post/reel by URL.",
98
+ "cost": 1,
99
+ "live": true,
100
+ "anyOf": [],
101
+ "params": [
102
+ {
103
+ "name": "url",
104
+ "type": "string",
105
+ "required": true,
106
+ "anyOf": false
107
+ },
108
+ {
109
+ "name": "handle",
110
+ "type": "string",
111
+ "required": false,
112
+ "anyOf": false
113
+ }
114
+ ]
115
+ },
116
+ {
117
+ "platform": "instagram",
118
+ "action": "user-reels",
119
+ "resource": "user/reels",
120
+ "version": "v1",
121
+ "method": "GET",
122
+ "path": "/v1/instagram/user/reels",
123
+ "toolName": "v1_instagram_user_reels",
124
+ "summary": "Get an Instagram user's reels by handle/user_id (paginated via max_id).",
125
+ "cost": 1,
126
+ "live": true,
127
+ "anyOf": [
128
+ [
129
+ "handle",
130
+ "user_id"
131
+ ]
132
+ ],
133
+ "params": [
134
+ {
135
+ "name": "handle",
136
+ "type": "string",
137
+ "required": false,
138
+ "anyOf": true
139
+ },
140
+ {
141
+ "name": "user_id",
142
+ "type": "string",
143
+ "required": false,
144
+ "anyOf": true
145
+ }
146
+ ]
147
+ },
148
+ {
149
+ "platform": "instagram",
150
+ "action": "post-comments",
151
+ "resource": "post/comments",
152
+ "version": "v2",
153
+ "method": "GET",
154
+ "path": "/v2/instagram/post/comments",
155
+ "toolName": "v2_instagram_post_comments",
156
+ "summary": "Get comments on an Instagram post by URL (paginated via next_min_id).",
157
+ "cost": 1,
158
+ "live": true,
159
+ "anyOf": [],
160
+ "params": [
161
+ {
162
+ "name": "url",
163
+ "type": "string",
164
+ "required": true,
165
+ "anyOf": false
166
+ },
167
+ {
168
+ "name": "handle",
169
+ "type": "string",
170
+ "required": false,
171
+ "anyOf": false
172
+ }
173
+ ]
174
+ },
175
+ {
176
+ "platform": "instagram",
177
+ "action": "media-transcript",
178
+ "resource": "media/transcript",
179
+ "version": "v2",
180
+ "method": "GET",
181
+ "path": "/v2/instagram/media/transcript",
182
+ "toolName": "v2_instagram_media_transcript",
183
+ "summary": "Get the transcript of an Instagram reel/video by URL.",
184
+ "cost": 1,
185
+ "live": true,
186
+ "anyOf": [],
187
+ "params": [
188
+ {
189
+ "name": "url",
190
+ "type": "string",
191
+ "required": true,
192
+ "anyOf": false
193
+ },
194
+ {
195
+ "name": "handle",
196
+ "type": "string",
197
+ "required": false,
198
+ "anyOf": false
199
+ }
200
+ ]
201
+ },
202
+ {
203
+ "platform": "instagram",
204
+ "action": "search-hashtag",
205
+ "resource": "search/hashtag",
206
+ "version": "v1",
207
+ "method": "GET",
208
+ "path": "/v1/instagram/search/hashtag",
209
+ "toolName": "v1_instagram_search_hashtag",
210
+ "summary": "Search Instagram posts by hashtag.",
211
+ "cost": 1,
212
+ "live": true,
213
+ "anyOf": [],
214
+ "params": [
215
+ {
216
+ "name": "hashtag",
217
+ "type": "string",
218
+ "required": true,
219
+ "anyOf": false
220
+ },
221
+ {
222
+ "name": "handle",
223
+ "type": "string",
224
+ "required": false,
225
+ "anyOf": false
226
+ }
227
+ ]
228
+ },
229
+ {
230
+ "platform": "instagram",
231
+ "action": "search-profiles",
232
+ "resource": "search/profiles",
233
+ "version": "v1",
234
+ "method": "GET",
235
+ "path": "/v1/instagram/search/profiles",
236
+ "toolName": "v1_instagram_search_profiles",
237
+ "summary": "Search Instagram profiles by query.",
238
+ "cost": 1,
239
+ "live": true,
240
+ "anyOf": [],
241
+ "params": [
242
+ {
243
+ "name": "query",
244
+ "type": "string",
245
+ "required": true,
246
+ "anyOf": false
247
+ },
248
+ {
249
+ "name": "handle",
250
+ "type": "string",
251
+ "required": false,
252
+ "anyOf": false
253
+ }
254
+ ]
255
+ },
256
+ {
257
+ "platform": "instagram",
258
+ "action": "reels-search",
259
+ "resource": "reels/search",
260
+ "version": "v2",
261
+ "method": "GET",
262
+ "path": "/v2/instagram/reels/search",
263
+ "toolName": "v2_instagram_reels_search",
264
+ "summary": "Search Instagram reels by query.",
265
+ "cost": 1,
266
+ "live": true,
267
+ "anyOf": [],
268
+ "params": [
269
+ {
270
+ "name": "query",
271
+ "type": "string",
272
+ "required": true,
273
+ "anyOf": false
274
+ },
275
+ {
276
+ "name": "handle",
277
+ "type": "string",
278
+ "required": false,
279
+ "anyOf": false
280
+ }
281
+ ]
282
+ },
283
+ {
284
+ "platform": "instagram",
285
+ "action": "audio-reels",
286
+ "resource": "audio/reels",
287
+ "version": "v1",
288
+ "method": "GET",
289
+ "path": "/v1/instagram/audio/reels",
290
+ "toolName": "v1_instagram_audio_reels",
291
+ "summary": "Get Instagram reels using a given audio/track (paginated via max_id).",
292
+ "cost": 1,
293
+ "live": true,
294
+ "anyOf": [],
295
+ "params": [
296
+ {
297
+ "name": "audio_id",
298
+ "type": "string",
299
+ "required": true,
300
+ "anyOf": false
301
+ },
302
+ {
303
+ "name": "handle",
304
+ "type": "string",
305
+ "required": false,
306
+ "anyOf": false
307
+ }
308
+ ]
309
+ },
310
+ {
311
+ "platform": "instagram",
312
+ "action": "reels-trending",
313
+ "resource": "reels/trending",
314
+ "version": "v1",
315
+ "method": "GET",
316
+ "path": "/v1/instagram/reels/trending",
317
+ "toolName": "v1_instagram_reels_trending",
318
+ "summary": "Get currently trending Instagram reels (paginated via max_id).",
319
+ "cost": 1,
320
+ "live": true,
321
+ "anyOf": [],
322
+ "params": [
323
+ {
324
+ "name": "handle",
325
+ "type": "string",
326
+ "required": false,
327
+ "anyOf": false
328
+ }
329
+ ]
330
+ },
331
+ {
332
+ "platform": "instagram",
333
+ "action": "user-highlights",
334
+ "resource": "user/highlights",
335
+ "version": "v1",
336
+ "method": "GET",
337
+ "path": "/v1/instagram/user/highlights",
338
+ "toolName": "v1_instagram_user_highlights",
339
+ "summary": "Get an Instagram user's story highlights by handle/user_id.",
340
+ "cost": 1,
341
+ "live": true,
342
+ "anyOf": [
343
+ [
344
+ "handle",
345
+ "user_id"
346
+ ]
347
+ ],
348
+ "params": [
349
+ {
350
+ "name": "handle",
351
+ "type": "string",
352
+ "required": false,
353
+ "anyOf": true
354
+ },
355
+ {
356
+ "name": "user_id",
357
+ "type": "string",
358
+ "required": false,
359
+ "anyOf": true
360
+ }
361
+ ]
362
+ },
363
+ {
364
+ "platform": "instagram",
365
+ "action": "user-highlight-detail",
366
+ "resource": "user/highlight/detail",
367
+ "version": "v1",
368
+ "method": "GET",
369
+ "path": "/v1/instagram/user/highlight/detail",
370
+ "toolName": "v1_instagram_user_highlight_detail",
371
+ "summary": "Get the media items of a single Instagram highlight by highlight_id.",
372
+ "cost": 1,
373
+ "live": true,
374
+ "anyOf": [],
375
+ "params": [
376
+ {
377
+ "name": "highlight_id",
378
+ "type": "string",
379
+ "required": true,
380
+ "anyOf": false
381
+ },
382
+ {
383
+ "name": "handle",
384
+ "type": "string",
385
+ "required": false,
386
+ "anyOf": false
387
+ }
388
+ ]
389
+ },
390
+ {
391
+ "platform": "instagram",
392
+ "action": "user-embed",
393
+ "resource": "user/embed",
394
+ "version": "v1",
395
+ "method": "GET",
396
+ "path": "/v1/instagram/user/embed",
397
+ "toolName": "v1_instagram_user_embed",
398
+ "summary": "Get an embeddable HTML representation of an Instagram profile by handle.",
399
+ "cost": 1,
400
+ "live": true,
401
+ "anyOf": [
402
+ [
403
+ "handle",
404
+ "url"
405
+ ]
406
+ ],
407
+ "params": [
408
+ {
409
+ "name": "handle",
410
+ "type": "string",
411
+ "required": false,
412
+ "anyOf": true
413
+ },
414
+ {
415
+ "name": "url",
416
+ "type": "string",
417
+ "required": false,
418
+ "anyOf": true
419
+ }
420
+ ]
421
+ },
422
+ {
423
+ "platform": "youtube",
424
+ "action": "channel",
425
+ "resource": "channel",
426
+ "version": "v1",
427
+ "method": "GET",
428
+ "path": "/v1/youtube/channel",
429
+ "toolName": "v1_youtube_channel",
430
+ "summary": "Get a YouTube channel's details by channelId, handle or url.",
431
+ "cost": 1,
432
+ "live": true,
433
+ "anyOf": [
434
+ [
435
+ "handle",
436
+ "channelId",
437
+ "url"
438
+ ]
439
+ ],
440
+ "params": [
441
+ {
442
+ "name": "handle",
443
+ "type": "string",
444
+ "required": false,
445
+ "anyOf": true
446
+ },
447
+ {
448
+ "name": "channelId",
449
+ "type": "string",
450
+ "required": false,
451
+ "anyOf": true
452
+ },
453
+ {
454
+ "name": "url",
455
+ "type": "string",
456
+ "required": false,
457
+ "anyOf": true
458
+ }
459
+ ]
460
+ },
461
+ {
462
+ "platform": "youtube",
463
+ "action": "channel-videos",
464
+ "resource": "channel-videos",
465
+ "version": "v1",
466
+ "method": "GET",
467
+ "path": "/v1/youtube/channel-videos",
468
+ "toolName": "v1_youtube_channel_videos",
469
+ "summary": "Get a YouTube channel's videos (paginated via continuationToken).",
470
+ "cost": 1,
471
+ "live": true,
472
+ "anyOf": [
473
+ [
474
+ "handle",
475
+ "channelId",
476
+ "url"
477
+ ]
478
+ ],
479
+ "params": [
480
+ {
481
+ "name": "handle",
482
+ "type": "string",
483
+ "required": false,
484
+ "anyOf": true
485
+ },
486
+ {
487
+ "name": "channelId",
488
+ "type": "string",
489
+ "required": false,
490
+ "anyOf": true
491
+ },
492
+ {
493
+ "name": "url",
494
+ "type": "string",
495
+ "required": false,
496
+ "anyOf": true
497
+ }
498
+ ]
499
+ },
500
+ {
501
+ "platform": "youtube",
502
+ "action": "channel-playlists",
503
+ "resource": "channel/playlists",
504
+ "version": "v1",
505
+ "method": "GET",
506
+ "path": "/v1/youtube/channel/playlists",
507
+ "toolName": "v1_youtube_channel_playlists",
508
+ "summary": "Get a YouTube channel's playlists.",
509
+ "cost": 1,
510
+ "live": true,
511
+ "anyOf": [
512
+ [
513
+ "handle",
514
+ "channelId",
515
+ "url"
516
+ ]
517
+ ],
518
+ "params": [
519
+ {
520
+ "name": "handle",
521
+ "type": "string",
522
+ "required": false,
523
+ "anyOf": true
524
+ },
525
+ {
526
+ "name": "channelId",
527
+ "type": "string",
528
+ "required": false,
529
+ "anyOf": true
530
+ },
531
+ {
532
+ "name": "url",
533
+ "type": "string",
534
+ "required": false,
535
+ "anyOf": true
536
+ }
537
+ ]
538
+ },
539
+ {
540
+ "platform": "youtube",
541
+ "action": "channel-lives",
542
+ "resource": "channel/lives",
543
+ "version": "v1",
544
+ "method": "GET",
545
+ "path": "/v1/youtube/channel/lives",
546
+ "toolName": "v1_youtube_channel_lives",
547
+ "summary": "Get a YouTube channel's live streams.",
548
+ "cost": 1,
549
+ "live": true,
550
+ "anyOf": [
551
+ [
552
+ "handle",
553
+ "channelId",
554
+ "url"
555
+ ]
556
+ ],
557
+ "params": [
558
+ {
559
+ "name": "handle",
560
+ "type": "string",
561
+ "required": false,
562
+ "anyOf": true
563
+ },
564
+ {
565
+ "name": "channelId",
566
+ "type": "string",
567
+ "required": false,
568
+ "anyOf": true
569
+ },
570
+ {
571
+ "name": "url",
572
+ "type": "string",
573
+ "required": false,
574
+ "anyOf": true
575
+ }
576
+ ]
577
+ },
578
+ {
579
+ "platform": "youtube",
580
+ "action": "channel-community-posts",
581
+ "resource": "channel/community-posts",
582
+ "version": "v1",
583
+ "method": "GET",
584
+ "path": "/v1/youtube/channel/community-posts",
585
+ "toolName": "v1_youtube_channel_community_posts",
586
+ "summary": "Get a YouTube channel's community posts.",
587
+ "cost": 1,
588
+ "live": true,
589
+ "anyOf": [
590
+ [
591
+ "handle",
592
+ "channelId",
593
+ "url"
594
+ ]
595
+ ],
596
+ "params": [
597
+ {
598
+ "name": "handle",
599
+ "type": "string",
600
+ "required": false,
601
+ "anyOf": true
602
+ },
603
+ {
604
+ "name": "channelId",
605
+ "type": "string",
606
+ "required": false,
607
+ "anyOf": true
608
+ },
609
+ {
610
+ "name": "url",
611
+ "type": "string",
612
+ "required": false,
613
+ "anyOf": true
614
+ }
615
+ ]
616
+ },
617
+ {
618
+ "platform": "youtube",
619
+ "action": "channel-shorts",
620
+ "resource": "channel/shorts",
621
+ "version": "v1",
622
+ "method": "GET",
623
+ "path": "/v1/youtube/channel/shorts",
624
+ "toolName": "v1_youtube_channel_shorts",
625
+ "summary": "Get a YouTube channel's shorts.",
626
+ "cost": 1,
627
+ "live": true,
628
+ "anyOf": [
629
+ [
630
+ "handle",
631
+ "channelId",
632
+ "url"
633
+ ]
634
+ ],
635
+ "params": [
636
+ {
637
+ "name": "handle",
638
+ "type": "string",
639
+ "required": false,
640
+ "anyOf": true
641
+ },
642
+ {
643
+ "name": "channelId",
644
+ "type": "string",
645
+ "required": false,
646
+ "anyOf": true
647
+ },
648
+ {
649
+ "name": "url",
650
+ "type": "string",
651
+ "required": false,
652
+ "anyOf": true
653
+ }
654
+ ]
655
+ },
656
+ {
657
+ "platform": "youtube",
658
+ "action": "video",
659
+ "resource": "video",
660
+ "version": "v1",
661
+ "method": "GET",
662
+ "path": "/v1/youtube/video",
663
+ "toolName": "v1_youtube_video",
664
+ "summary": "Get details of a YouTube video or short.",
665
+ "cost": 1,
666
+ "live": true,
667
+ "anyOf": [],
668
+ "params": [
669
+ {
670
+ "name": "url",
671
+ "type": "string",
672
+ "required": true,
673
+ "anyOf": false
674
+ },
675
+ {
676
+ "name": "handle",
677
+ "type": "string",
678
+ "required": false,
679
+ "anyOf": false
680
+ },
681
+ {
682
+ "name": "channelId",
683
+ "type": "string",
684
+ "required": false,
685
+ "anyOf": false
686
+ }
687
+ ]
688
+ },
689
+ {
690
+ "platform": "youtube",
691
+ "action": "video-transcript",
692
+ "resource": "video/transcript",
693
+ "version": "v1",
694
+ "method": "GET",
695
+ "path": "/v1/youtube/video/transcript",
696
+ "toolName": "v1_youtube_video_transcript",
697
+ "summary": "Get the transcript of a YouTube video or short.",
698
+ "cost": 1,
699
+ "live": true,
700
+ "anyOf": [],
701
+ "params": [
702
+ {
703
+ "name": "url",
704
+ "type": "string",
705
+ "required": true,
706
+ "anyOf": false
707
+ },
708
+ {
709
+ "name": "handle",
710
+ "type": "string",
711
+ "required": false,
712
+ "anyOf": false
713
+ },
714
+ {
715
+ "name": "channelId",
716
+ "type": "string",
717
+ "required": false,
718
+ "anyOf": false
719
+ }
720
+ ]
721
+ },
722
+ {
723
+ "platform": "youtube",
724
+ "action": "video-sponsors",
725
+ "resource": "video/sponsors",
726
+ "version": "v1",
727
+ "method": "GET",
728
+ "path": "/v1/youtube/video/sponsors",
729
+ "toolName": "v1_youtube_video_sponsors",
730
+ "summary": "Detect suspected sponsors of a YouTube video.",
731
+ "cost": 1,
732
+ "live": true,
733
+ "anyOf": [],
734
+ "params": [
735
+ {
736
+ "name": "url",
737
+ "type": "string",
738
+ "required": true,
739
+ "anyOf": false
740
+ },
741
+ {
742
+ "name": "handle",
743
+ "type": "string",
744
+ "required": false,
745
+ "anyOf": false
746
+ },
747
+ {
748
+ "name": "channelId",
749
+ "type": "string",
750
+ "required": false,
751
+ "anyOf": false
752
+ }
753
+ ]
754
+ },
755
+ {
756
+ "platform": "youtube",
757
+ "action": "search",
758
+ "resource": "search",
759
+ "version": "v1",
760
+ "method": "GET",
761
+ "path": "/v1/youtube/search",
762
+ "toolName": "v1_youtube_search",
763
+ "summary": "Search YouTube (videos, channels, playlists, shorts).",
764
+ "cost": 1,
765
+ "live": true,
766
+ "anyOf": [],
767
+ "params": [
768
+ {
769
+ "name": "query",
770
+ "type": "string",
771
+ "required": true,
772
+ "anyOf": false
773
+ },
774
+ {
775
+ "name": "handle",
776
+ "type": "string",
777
+ "required": false,
778
+ "anyOf": false
779
+ },
780
+ {
781
+ "name": "channelId",
782
+ "type": "string",
783
+ "required": false,
784
+ "anyOf": false
785
+ }
786
+ ]
787
+ },
788
+ {
789
+ "platform": "youtube",
790
+ "action": "search-hashtag",
791
+ "resource": "search/hashtag",
792
+ "version": "v1",
793
+ "method": "GET",
794
+ "path": "/v1/youtube/search/hashtag",
795
+ "toolName": "v1_youtube_search_hashtag",
796
+ "summary": "Search YouTube by hashtag.",
797
+ "cost": 1,
798
+ "live": true,
799
+ "anyOf": [],
800
+ "params": [
801
+ {
802
+ "name": "hashtag",
803
+ "type": "string",
804
+ "required": true,
805
+ "anyOf": false
806
+ },
807
+ {
808
+ "name": "handle",
809
+ "type": "string",
810
+ "required": false,
811
+ "anyOf": false
812
+ },
813
+ {
814
+ "name": "channelId",
815
+ "type": "string",
816
+ "required": false,
817
+ "anyOf": false
818
+ }
819
+ ]
820
+ },
821
+ {
822
+ "platform": "youtube",
823
+ "action": "video-comments",
824
+ "resource": "video/comments",
825
+ "version": "v1",
826
+ "method": "GET",
827
+ "path": "/v1/youtube/video/comments",
828
+ "toolName": "v1_youtube_video_comments",
829
+ "summary": "Get comments of a YouTube video.",
830
+ "cost": 1,
831
+ "live": true,
832
+ "anyOf": [],
833
+ "params": [
834
+ {
835
+ "name": "url",
836
+ "type": "string",
837
+ "required": true,
838
+ "anyOf": false
839
+ },
840
+ {
841
+ "name": "handle",
842
+ "type": "string",
843
+ "required": false,
844
+ "anyOf": false
845
+ },
846
+ {
847
+ "name": "channelId",
848
+ "type": "string",
849
+ "required": false,
850
+ "anyOf": false
851
+ }
852
+ ]
853
+ },
854
+ {
855
+ "platform": "youtube",
856
+ "action": "video-comment-replies",
857
+ "resource": "video/comment/replies",
858
+ "version": "v1",
859
+ "method": "GET",
860
+ "path": "/v1/youtube/video/comment/replies",
861
+ "toolName": "v1_youtube_video_comment_replies",
862
+ "summary": "Get replies to a YouTube comment.",
863
+ "cost": 1,
864
+ "live": true,
865
+ "anyOf": [],
866
+ "params": [
867
+ {
868
+ "name": "continuationToken",
869
+ "type": "string",
870
+ "required": true,
871
+ "anyOf": false
872
+ },
873
+ {
874
+ "name": "handle",
875
+ "type": "string",
876
+ "required": false,
877
+ "anyOf": false
878
+ },
879
+ {
880
+ "name": "channelId",
881
+ "type": "string",
882
+ "required": false,
883
+ "anyOf": false
884
+ }
885
+ ]
886
+ },
887
+ {
888
+ "platform": "youtube",
889
+ "action": "shorts-trending",
890
+ "resource": "shorts/trending",
891
+ "version": "v1",
892
+ "method": "GET",
893
+ "path": "/v1/youtube/shorts/trending",
894
+ "toolName": "v1_youtube_shorts_trending",
895
+ "summary": "Get trending YouTube shorts.",
896
+ "cost": 1,
897
+ "live": true,
898
+ "anyOf": [],
899
+ "params": [
900
+ {
901
+ "name": "handle",
902
+ "type": "string",
903
+ "required": false,
904
+ "anyOf": false
905
+ },
906
+ {
907
+ "name": "channelId",
908
+ "type": "string",
909
+ "required": false,
910
+ "anyOf": false
911
+ }
912
+ ]
913
+ },
914
+ {
915
+ "platform": "youtube",
916
+ "action": "playlist",
917
+ "resource": "playlist",
918
+ "version": "v1",
919
+ "method": "GET",
920
+ "path": "/v1/youtube/playlist",
921
+ "toolName": "v1_youtube_playlist",
922
+ "summary": "Get the videos of a YouTube playlist.",
923
+ "cost": 1,
924
+ "live": true,
925
+ "anyOf": [],
926
+ "params": [
927
+ {
928
+ "name": "playlist_id",
929
+ "type": "string",
930
+ "required": true,
931
+ "anyOf": false
932
+ },
933
+ {
934
+ "name": "handle",
935
+ "type": "string",
936
+ "required": false,
937
+ "anyOf": false
938
+ },
939
+ {
940
+ "name": "channelId",
941
+ "type": "string",
942
+ "required": false,
943
+ "anyOf": false
944
+ }
945
+ ]
946
+ },
947
+ {
948
+ "platform": "youtube",
949
+ "action": "community-post",
950
+ "resource": "community-post",
951
+ "version": "v1",
952
+ "method": "GET",
953
+ "path": "/v1/youtube/community-post",
954
+ "toolName": "v1_youtube_community_post",
955
+ "summary": "Get details of a YouTube community post.",
956
+ "cost": 1,
957
+ "live": true,
958
+ "anyOf": [],
959
+ "params": [
960
+ {
961
+ "name": "url",
962
+ "type": "string",
963
+ "required": true,
964
+ "anyOf": false
965
+ },
966
+ {
967
+ "name": "handle",
968
+ "type": "string",
969
+ "required": false,
970
+ "anyOf": false
971
+ },
972
+ {
973
+ "name": "channelId",
974
+ "type": "string",
975
+ "required": false,
976
+ "anyOf": false
977
+ }
978
+ ]
979
+ },
980
+ {
981
+ "platform": "facebook",
982
+ "action": "profile",
983
+ "resource": "profile",
984
+ "version": "v1",
985
+ "method": "GET",
986
+ "path": "/v1/facebook/profile",
987
+ "toolName": "v1_facebook_profile",
988
+ "summary": "Get a Facebook page/profile by URL (name, category, photos, likes, followers).",
989
+ "cost": 1,
990
+ "live": true,
991
+ "anyOf": [
992
+ [
993
+ "url",
994
+ "pageId",
995
+ "handle"
996
+ ]
997
+ ],
998
+ "params": [
999
+ {
1000
+ "name": "url",
1001
+ "type": "string",
1002
+ "required": false,
1003
+ "anyOf": true
1004
+ },
1005
+ {
1006
+ "name": "pageId",
1007
+ "type": "string",
1008
+ "required": false,
1009
+ "anyOf": true
1010
+ },
1011
+ {
1012
+ "name": "handle",
1013
+ "type": "string",
1014
+ "required": false,
1015
+ "anyOf": true
1016
+ }
1017
+ ]
1018
+ },
1019
+ {
1020
+ "platform": "facebook",
1021
+ "action": "profile-posts",
1022
+ "resource": "profile/posts",
1023
+ "version": "v1",
1024
+ "method": "GET",
1025
+ "path": "/v1/facebook/profile/posts",
1026
+ "toolName": "v1_facebook_profile_posts",
1027
+ "summary": "Get a Facebook page's posts (message, reactions, comments, shares); paginate via cursor.",
1028
+ "cost": 1,
1029
+ "live": true,
1030
+ "anyOf": [
1031
+ [
1032
+ "url",
1033
+ "pageId",
1034
+ "handle"
1035
+ ]
1036
+ ],
1037
+ "params": [
1038
+ {
1039
+ "name": "url",
1040
+ "type": "string",
1041
+ "required": false,
1042
+ "anyOf": true
1043
+ },
1044
+ {
1045
+ "name": "pageId",
1046
+ "type": "string",
1047
+ "required": false,
1048
+ "anyOf": true
1049
+ },
1050
+ {
1051
+ "name": "handle",
1052
+ "type": "string",
1053
+ "required": false,
1054
+ "anyOf": true
1055
+ }
1056
+ ]
1057
+ },
1058
+ {
1059
+ "platform": "facebook",
1060
+ "action": "post",
1061
+ "resource": "post",
1062
+ "version": "v1",
1063
+ "method": "GET",
1064
+ "path": "/v1/facebook/post",
1065
+ "toolName": "v1_facebook_post",
1066
+ "summary": "Get a single Facebook post by URL (message, reactions, comments, shares, video).",
1067
+ "cost": 1,
1068
+ "live": true,
1069
+ "anyOf": [
1070
+ [
1071
+ "url"
1072
+ ]
1073
+ ],
1074
+ "params": [
1075
+ {
1076
+ "name": "url",
1077
+ "type": "string",
1078
+ "required": false,
1079
+ "anyOf": true
1080
+ }
1081
+ ]
1082
+ },
1083
+ {
1084
+ "platform": "facebook",
1085
+ "action": "post-comments",
1086
+ "resource": "post/comments",
1087
+ "version": "v1",
1088
+ "method": "GET",
1089
+ "path": "/v1/facebook/post/comments",
1090
+ "toolName": "v1_facebook_post_comments",
1091
+ "summary": "Get the comments on a Facebook post by URL (embedded top comments; full list needs FB_SESSION).",
1092
+ "cost": 1,
1093
+ "live": true,
1094
+ "anyOf": [
1095
+ [
1096
+ "url"
1097
+ ]
1098
+ ],
1099
+ "params": [
1100
+ {
1101
+ "name": "url",
1102
+ "type": "string",
1103
+ "required": false,
1104
+ "anyOf": true
1105
+ }
1106
+ ]
1107
+ },
1108
+ {
1109
+ "platform": "facebook",
1110
+ "action": "profile-reels",
1111
+ "resource": "profile/reels",
1112
+ "version": "v1",
1113
+ "method": "GET",
1114
+ "path": "/v1/facebook/profile/reels",
1115
+ "toolName": "v1_facebook_profile_reels",
1116
+ "summary": "Get a Facebook page's videos/reels (id, title, thumbnail, play count).",
1117
+ "cost": 1,
1118
+ "live": true,
1119
+ "anyOf": [
1120
+ [
1121
+ "url",
1122
+ "pageId",
1123
+ "handle"
1124
+ ]
1125
+ ],
1126
+ "params": [
1127
+ {
1128
+ "name": "url",
1129
+ "type": "string",
1130
+ "required": false,
1131
+ "anyOf": true
1132
+ },
1133
+ {
1134
+ "name": "pageId",
1135
+ "type": "string",
1136
+ "required": false,
1137
+ "anyOf": true
1138
+ },
1139
+ {
1140
+ "name": "handle",
1141
+ "type": "string",
1142
+ "required": false,
1143
+ "anyOf": true
1144
+ }
1145
+ ]
1146
+ },
1147
+ {
1148
+ "platform": "facebook",
1149
+ "action": "profile-photos",
1150
+ "resource": "profile/photos",
1151
+ "version": "v1",
1152
+ "method": "GET",
1153
+ "path": "/v1/facebook/profile/photos",
1154
+ "toolName": "v1_facebook_profile_photos",
1155
+ "summary": "Get a Facebook page's photos (id, image url, link).",
1156
+ "cost": 1,
1157
+ "live": true,
1158
+ "anyOf": [
1159
+ [
1160
+ "url",
1161
+ "pageId",
1162
+ "handle"
1163
+ ]
1164
+ ],
1165
+ "params": [
1166
+ {
1167
+ "name": "url",
1168
+ "type": "string",
1169
+ "required": false,
1170
+ "anyOf": true
1171
+ },
1172
+ {
1173
+ "name": "pageId",
1174
+ "type": "string",
1175
+ "required": false,
1176
+ "anyOf": true
1177
+ },
1178
+ {
1179
+ "name": "handle",
1180
+ "type": "string",
1181
+ "required": false,
1182
+ "anyOf": true
1183
+ }
1184
+ ]
1185
+ },
1186
+ {
1187
+ "platform": "facebook",
1188
+ "action": "profile-events",
1189
+ "resource": "profile/events",
1190
+ "version": "v1",
1191
+ "method": "GET",
1192
+ "path": "/v1/facebook/profile/events",
1193
+ "toolName": "v1_facebook_profile_events",
1194
+ "summary": "Get a Facebook page's events.",
1195
+ "cost": 1,
1196
+ "live": false,
1197
+ "anyOf": [
1198
+ [
1199
+ "url",
1200
+ "pageId"
1201
+ ]
1202
+ ],
1203
+ "params": [
1204
+ {
1205
+ "name": "url",
1206
+ "type": "string",
1207
+ "required": false,
1208
+ "anyOf": true
1209
+ },
1210
+ {
1211
+ "name": "pageId",
1212
+ "type": "string",
1213
+ "required": false,
1214
+ "anyOf": true
1215
+ }
1216
+ ]
1217
+ },
1218
+ {
1219
+ "platform": "facebook",
1220
+ "action": "post-transcript",
1221
+ "resource": "post/transcript",
1222
+ "version": "v1",
1223
+ "method": "GET",
1224
+ "path": "/v1/facebook/post/transcript",
1225
+ "toolName": "v1_facebook_post_transcript",
1226
+ "summary": "Get the transcript of a Facebook video post by URL.",
1227
+ "cost": 1,
1228
+ "live": false,
1229
+ "anyOf": [],
1230
+ "params": [
1231
+ {
1232
+ "name": "url",
1233
+ "type": "string",
1234
+ "required": true,
1235
+ "anyOf": false
1236
+ }
1237
+ ]
1238
+ },
1239
+ {
1240
+ "platform": "facebook",
1241
+ "action": "post-comment-replies",
1242
+ "resource": "post/comment/replies",
1243
+ "version": "v1",
1244
+ "method": "GET",
1245
+ "path": "/v1/facebook/post/comment/replies",
1246
+ "toolName": "v1_facebook_post_comment_replies",
1247
+ "summary": "Get replies to a Facebook comment (paginated).",
1248
+ "cost": 1,
1249
+ "live": false,
1250
+ "anyOf": [
1251
+ [
1252
+ "url",
1253
+ "commentId"
1254
+ ]
1255
+ ],
1256
+ "params": [
1257
+ {
1258
+ "name": "url",
1259
+ "type": "string",
1260
+ "required": false,
1261
+ "anyOf": true
1262
+ },
1263
+ {
1264
+ "name": "commentId",
1265
+ "type": "string",
1266
+ "required": false,
1267
+ "anyOf": true
1268
+ }
1269
+ ]
1270
+ },
1271
+ {
1272
+ "platform": "facebook",
1273
+ "action": "group-posts",
1274
+ "resource": "group/posts",
1275
+ "version": "v1",
1276
+ "method": "GET",
1277
+ "path": "/v1/facebook/group/posts",
1278
+ "toolName": "v1_facebook_group_posts",
1279
+ "summary": "Get a public Facebook group's posts (paginated via cursor).",
1280
+ "cost": 1,
1281
+ "live": false,
1282
+ "anyOf": [
1283
+ [
1284
+ "url",
1285
+ "groupId"
1286
+ ]
1287
+ ],
1288
+ "params": [
1289
+ {
1290
+ "name": "url",
1291
+ "type": "string",
1292
+ "required": false,
1293
+ "anyOf": true
1294
+ },
1295
+ {
1296
+ "name": "groupId",
1297
+ "type": "string",
1298
+ "required": false,
1299
+ "anyOf": true
1300
+ }
1301
+ ]
1302
+ },
1303
+ {
1304
+ "platform": "facebook",
1305
+ "action": "marketplace-location-search",
1306
+ "resource": "marketplace/location/search",
1307
+ "version": "v1",
1308
+ "method": "GET",
1309
+ "path": "/v1/facebook/marketplace/location/search",
1310
+ "toolName": "v1_facebook_marketplace_location_search",
1311
+ "summary": "Search Facebook Marketplace locations by query.",
1312
+ "cost": 1,
1313
+ "live": false,
1314
+ "anyOf": [],
1315
+ "params": [
1316
+ {
1317
+ "name": "query",
1318
+ "type": "string",
1319
+ "required": true,
1320
+ "anyOf": false
1321
+ },
1322
+ {
1323
+ "name": "url",
1324
+ "type": "string",
1325
+ "required": false,
1326
+ "anyOf": false
1327
+ }
1328
+ ]
1329
+ },
1330
+ {
1331
+ "platform": "facebook",
1332
+ "action": "marketplace-search",
1333
+ "resource": "marketplace/search",
1334
+ "version": "v1",
1335
+ "method": "GET",
1336
+ "path": "/v1/facebook/marketplace/search",
1337
+ "toolName": "v1_facebook_marketplace_search",
1338
+ "summary": "Search Facebook Marketplace listings.",
1339
+ "cost": 1,
1340
+ "live": false,
1341
+ "anyOf": [],
1342
+ "params": [
1343
+ {
1344
+ "name": "query",
1345
+ "type": "string",
1346
+ "required": true,
1347
+ "anyOf": false
1348
+ },
1349
+ {
1350
+ "name": "url",
1351
+ "type": "string",
1352
+ "required": false,
1353
+ "anyOf": false
1354
+ }
1355
+ ]
1356
+ },
1357
+ {
1358
+ "platform": "facebook",
1359
+ "action": "marketplace-item",
1360
+ "resource": "marketplace/item",
1361
+ "version": "v1",
1362
+ "method": "GET",
1363
+ "path": "/v1/facebook/marketplace/item",
1364
+ "toolName": "v1_facebook_marketplace_item",
1365
+ "summary": "Get a single Facebook Marketplace item by URL/id.",
1366
+ "cost": 1,
1367
+ "live": false,
1368
+ "anyOf": [
1369
+ [
1370
+ "url",
1371
+ "id"
1372
+ ]
1373
+ ],
1374
+ "params": [
1375
+ {
1376
+ "name": "url",
1377
+ "type": "string",
1378
+ "required": false,
1379
+ "anyOf": true
1380
+ },
1381
+ {
1382
+ "name": "id",
1383
+ "type": "string",
1384
+ "required": false,
1385
+ "anyOf": true
1386
+ }
1387
+ ]
1388
+ },
1389
+ {
1390
+ "platform": "facebook",
1391
+ "action": "events-search",
1392
+ "resource": "events/search",
1393
+ "version": "v1",
1394
+ "method": "GET",
1395
+ "path": "/v1/facebook/events/search",
1396
+ "toolName": "v1_facebook_events_search",
1397
+ "summary": "Search Facebook events by query.",
1398
+ "cost": 1,
1399
+ "live": false,
1400
+ "anyOf": [],
1401
+ "params": [
1402
+ {
1403
+ "name": "query",
1404
+ "type": "string",
1405
+ "required": true,
1406
+ "anyOf": false
1407
+ },
1408
+ {
1409
+ "name": "url",
1410
+ "type": "string",
1411
+ "required": false,
1412
+ "anyOf": false
1413
+ }
1414
+ ]
1415
+ },
1416
+ {
1417
+ "platform": "facebook",
1418
+ "action": "events",
1419
+ "resource": "events",
1420
+ "version": "v1",
1421
+ "method": "GET",
1422
+ "path": "/v1/facebook/events",
1423
+ "toolName": "v1_facebook_events",
1424
+ "summary": "Get Facebook events for a location/query.",
1425
+ "cost": 1,
1426
+ "live": false,
1427
+ "anyOf": [
1428
+ [
1429
+ "query",
1430
+ "location"
1431
+ ]
1432
+ ],
1433
+ "params": [
1434
+ {
1435
+ "name": "query",
1436
+ "type": "string",
1437
+ "required": false,
1438
+ "anyOf": true
1439
+ },
1440
+ {
1441
+ "name": "location",
1442
+ "type": "string",
1443
+ "required": false,
1444
+ "anyOf": true
1445
+ },
1446
+ {
1447
+ "name": "url",
1448
+ "type": "string",
1449
+ "required": false,
1450
+ "anyOf": false
1451
+ }
1452
+ ]
1453
+ },
1454
+ {
1455
+ "platform": "facebook",
1456
+ "action": "event-details",
1457
+ "resource": "event/details",
1458
+ "version": "v1",
1459
+ "method": "GET",
1460
+ "path": "/v1/facebook/event/details",
1461
+ "toolName": "v1_facebook_event_details",
1462
+ "summary": "Get details of a single Facebook event by URL/id.",
1463
+ "cost": 1,
1464
+ "live": false,
1465
+ "anyOf": [
1466
+ [
1467
+ "url",
1468
+ "id"
1469
+ ]
1470
+ ],
1471
+ "params": [
1472
+ {
1473
+ "name": "url",
1474
+ "type": "string",
1475
+ "required": false,
1476
+ "anyOf": true
1477
+ },
1478
+ {
1479
+ "name": "id",
1480
+ "type": "string",
1481
+ "required": false,
1482
+ "anyOf": true
1483
+ }
1484
+ ]
1485
+ },
1486
+ {
1487
+ "platform": "facebook",
1488
+ "action": "adLibrary-ad",
1489
+ "resource": "adLibrary/ad",
1490
+ "version": "v1",
1491
+ "method": "GET",
1492
+ "path": "/v1/facebook/adLibrary/ad",
1493
+ "toolName": "v1_facebook_adLibrary_ad",
1494
+ "summary": "Get details of a Facebook Ad Library ad by id/URL.",
1495
+ "cost": 1,
1496
+ "live": false,
1497
+ "anyOf": [
1498
+ [
1499
+ "url",
1500
+ "id"
1501
+ ]
1502
+ ],
1503
+ "params": [
1504
+ {
1505
+ "name": "url",
1506
+ "type": "string",
1507
+ "required": false,
1508
+ "anyOf": true
1509
+ },
1510
+ {
1511
+ "name": "id",
1512
+ "type": "string",
1513
+ "required": false,
1514
+ "anyOf": true
1515
+ }
1516
+ ]
1517
+ },
1518
+ {
1519
+ "platform": "facebook",
1520
+ "action": "adLibrary-ad-transcript",
1521
+ "resource": "adLibrary/ad/transcript",
1522
+ "version": "v1",
1523
+ "method": "GET",
1524
+ "path": "/v1/facebook/adLibrary/ad/transcript",
1525
+ "toolName": "v1_facebook_adLibrary_ad_transcript",
1526
+ "summary": "Get the transcript of a Facebook Ad Library video ad.",
1527
+ "cost": 1,
1528
+ "live": false,
1529
+ "anyOf": [
1530
+ [
1531
+ "url",
1532
+ "id"
1533
+ ]
1534
+ ],
1535
+ "params": [
1536
+ {
1537
+ "name": "url",
1538
+ "type": "string",
1539
+ "required": false,
1540
+ "anyOf": true
1541
+ },
1542
+ {
1543
+ "name": "id",
1544
+ "type": "string",
1545
+ "required": false,
1546
+ "anyOf": true
1547
+ }
1548
+ ]
1549
+ },
1550
+ {
1551
+ "platform": "facebook",
1552
+ "action": "adLibrary-search-ads",
1553
+ "resource": "adLibrary/search/ads",
1554
+ "version": "v1",
1555
+ "method": "GET",
1556
+ "path": "/v1/facebook/adLibrary/search/ads",
1557
+ "toolName": "v1_facebook_adLibrary_search_ads",
1558
+ "summary": "Search Facebook Ad Library ads.",
1559
+ "cost": 1,
1560
+ "live": false,
1561
+ "anyOf": [
1562
+ [
1563
+ "query",
1564
+ "pageId"
1565
+ ]
1566
+ ],
1567
+ "params": [
1568
+ {
1569
+ "name": "query",
1570
+ "type": "string",
1571
+ "required": false,
1572
+ "anyOf": true
1573
+ },
1574
+ {
1575
+ "name": "pageId",
1576
+ "type": "string",
1577
+ "required": false,
1578
+ "anyOf": true
1579
+ },
1580
+ {
1581
+ "name": "url",
1582
+ "type": "string",
1583
+ "required": false,
1584
+ "anyOf": false
1585
+ }
1586
+ ]
1587
+ },
1588
+ {
1589
+ "platform": "facebook",
1590
+ "action": "adLibrary-company-ads",
1591
+ "resource": "adLibrary/company/ads",
1592
+ "version": "v1",
1593
+ "method": "GET",
1594
+ "path": "/v1/facebook/adLibrary/company/ads",
1595
+ "toolName": "v1_facebook_adLibrary_company_ads",
1596
+ "summary": "Get all ads a company is running in the Facebook Ad Library.",
1597
+ "cost": 1,
1598
+ "live": false,
1599
+ "anyOf": [
1600
+ [
1601
+ "pageId",
1602
+ "url"
1603
+ ]
1604
+ ],
1605
+ "params": [
1606
+ {
1607
+ "name": "pageId",
1608
+ "type": "string",
1609
+ "required": false,
1610
+ "anyOf": true
1611
+ },
1612
+ {
1613
+ "name": "url",
1614
+ "type": "string",
1615
+ "required": false,
1616
+ "anyOf": true
1617
+ }
1618
+ ]
1619
+ },
1620
+ {
1621
+ "platform": "facebook",
1622
+ "action": "adLibrary-search-companies",
1623
+ "resource": "adLibrary/search/companies",
1624
+ "version": "v1",
1625
+ "method": "GET",
1626
+ "path": "/v1/facebook/adLibrary/search/companies",
1627
+ "toolName": "v1_facebook_adLibrary_search_companies",
1628
+ "summary": "Search companies in the Facebook Ad Library.",
1629
+ "cost": 1,
1630
+ "live": false,
1631
+ "anyOf": [],
1632
+ "params": [
1633
+ {
1634
+ "name": "query",
1635
+ "type": "string",
1636
+ "required": true,
1637
+ "anyOf": false
1638
+ },
1639
+ {
1640
+ "name": "url",
1641
+ "type": "string",
1642
+ "required": false,
1643
+ "anyOf": false
1644
+ }
1645
+ ]
1646
+ },
1647
+ {
1648
+ "platform": "linkedin",
1649
+ "action": "profile",
1650
+ "resource": "profile",
1651
+ "version": "v1",
1652
+ "method": "GET",
1653
+ "path": "/v1/linkedin/profile",
1654
+ "toolName": "v1_linkedin_profile",
1655
+ "summary": "Get a LinkedIn member's public profile by URL.",
1656
+ "cost": 1,
1657
+ "live": true,
1658
+ "anyOf": [],
1659
+ "params": [
1660
+ {
1661
+ "name": "url",
1662
+ "type": "string",
1663
+ "required": true,
1664
+ "anyOf": false
1665
+ }
1666
+ ]
1667
+ },
1668
+ {
1669
+ "platform": "linkedin",
1670
+ "action": "company",
1671
+ "resource": "company",
1672
+ "version": "v1",
1673
+ "method": "GET",
1674
+ "path": "/v1/linkedin/company",
1675
+ "toolName": "v1_linkedin_company",
1676
+ "summary": "Get a LinkedIn company page by URL.",
1677
+ "cost": 1,
1678
+ "live": true,
1679
+ "anyOf": [],
1680
+ "params": [
1681
+ {
1682
+ "name": "url",
1683
+ "type": "string",
1684
+ "required": true,
1685
+ "anyOf": false
1686
+ }
1687
+ ]
1688
+ },
1689
+ {
1690
+ "platform": "linkedin",
1691
+ "action": "company-posts",
1692
+ "resource": "company/posts",
1693
+ "version": "v1",
1694
+ "method": "GET",
1695
+ "path": "/v1/linkedin/company/posts",
1696
+ "toolName": "v1_linkedin_company_posts",
1697
+ "summary": "Get a LinkedIn company's posts (page 1 via guest; deeper pages via Voyager).",
1698
+ "cost": 1,
1699
+ "live": true,
1700
+ "anyOf": [],
1701
+ "params": [
1702
+ {
1703
+ "name": "url",
1704
+ "type": "string",
1705
+ "required": true,
1706
+ "anyOf": false
1707
+ }
1708
+ ]
1709
+ },
1710
+ {
1711
+ "platform": "linkedin",
1712
+ "action": "post",
1713
+ "resource": "post",
1714
+ "version": "v1",
1715
+ "method": "GET",
1716
+ "path": "/v1/linkedin/post",
1717
+ "toolName": "v1_linkedin_post",
1718
+ "summary": "Get a single LinkedIn post or article by URL.",
1719
+ "cost": 1,
1720
+ "live": true,
1721
+ "anyOf": [],
1722
+ "params": [
1723
+ {
1724
+ "name": "url",
1725
+ "type": "string",
1726
+ "required": true,
1727
+ "anyOf": false
1728
+ }
1729
+ ]
1730
+ },
1731
+ {
1732
+ "platform": "linkedin",
1733
+ "action": "post-transcript",
1734
+ "resource": "post/transcript",
1735
+ "version": "v1",
1736
+ "method": "GET",
1737
+ "path": "/v1/linkedin/post/transcript",
1738
+ "toolName": "v1_linkedin_post_transcript",
1739
+ "summary": "Get the transcript of a LinkedIn video post by URL.",
1740
+ "cost": 1,
1741
+ "live": true,
1742
+ "anyOf": [],
1743
+ "params": [
1744
+ {
1745
+ "name": "url",
1746
+ "type": "string",
1747
+ "required": true,
1748
+ "anyOf": false
1749
+ }
1750
+ ]
1751
+ },
1752
+ {
1753
+ "platform": "linkedin",
1754
+ "action": "search-posts",
1755
+ "resource": "search/posts",
1756
+ "version": "v1",
1757
+ "method": "GET",
1758
+ "path": "/v1/linkedin/search/posts",
1759
+ "toolName": "v1_linkedin_search_posts",
1760
+ "summary": "Search LinkedIn posts by keyword (authenticated Voyager API).",
1761
+ "cost": 1,
1762
+ "live": true,
1763
+ "anyOf": [],
1764
+ "params": [
1765
+ {
1766
+ "name": "query",
1767
+ "type": "string",
1768
+ "required": true,
1769
+ "anyOf": false
1770
+ },
1771
+ {
1772
+ "name": "url",
1773
+ "type": "string",
1774
+ "required": false,
1775
+ "anyOf": false
1776
+ }
1777
+ ]
1778
+ },
1779
+ {
1780
+ "platform": "twitter",
1781
+ "action": "profile",
1782
+ "resource": "profile",
1783
+ "version": "v1",
1784
+ "method": "GET",
1785
+ "path": "/v1/twitter/profile",
1786
+ "toolName": "v1_twitter_profile",
1787
+ "summary": "Get an X/Twitter user's profile (stats, verification, metadata) by handle.",
1788
+ "cost": 1,
1789
+ "live": true,
1790
+ "anyOf": [
1791
+ [
1792
+ "handle"
1793
+ ]
1794
+ ],
1795
+ "params": [
1796
+ {
1797
+ "name": "handle",
1798
+ "type": "string",
1799
+ "required": false,
1800
+ "anyOf": true
1801
+ }
1802
+ ]
1803
+ },
1804
+ {
1805
+ "platform": "twitter",
1806
+ "action": "user-tweets",
1807
+ "resource": "user-tweets",
1808
+ "version": "v1",
1809
+ "method": "GET",
1810
+ "path": "/v1/twitter/user-tweets",
1811
+ "toolName": "v1_twitter_user_tweets",
1812
+ "summary": "Get an X/Twitter user's tweets by handle. Guest access returns the ~100 most popular tweets.",
1813
+ "cost": 1,
1814
+ "live": true,
1815
+ "anyOf": [
1816
+ [
1817
+ "handle"
1818
+ ]
1819
+ ],
1820
+ "params": [
1821
+ {
1822
+ "name": "handle",
1823
+ "type": "string",
1824
+ "required": false,
1825
+ "anyOf": true
1826
+ }
1827
+ ]
1828
+ },
1829
+ {
1830
+ "platform": "twitter",
1831
+ "action": "tweet",
1832
+ "resource": "tweet",
1833
+ "version": "v1",
1834
+ "method": "GET",
1835
+ "path": "/v1/twitter/tweet",
1836
+ "toolName": "v1_twitter_tweet",
1837
+ "summary": "Get a single X/Twitter tweet (content + engagement + author) by URL.",
1838
+ "cost": 1,
1839
+ "live": true,
1840
+ "anyOf": [],
1841
+ "params": [
1842
+ {
1843
+ "name": "url",
1844
+ "type": "string",
1845
+ "required": true,
1846
+ "anyOf": false
1847
+ },
1848
+ {
1849
+ "name": "handle",
1850
+ "type": "string",
1851
+ "required": false,
1852
+ "anyOf": false
1853
+ }
1854
+ ]
1855
+ },
1856
+ {
1857
+ "platform": "twitter",
1858
+ "action": "tweet-transcript",
1859
+ "resource": "tweet/transcript",
1860
+ "version": "v1",
1861
+ "method": "GET",
1862
+ "path": "/v1/twitter/tweet/transcript",
1863
+ "toolName": "v1_twitter_tweet_transcript",
1864
+ "summary": "Get the transcript of a video tweet by URL.",
1865
+ "cost": 1,
1866
+ "live": false,
1867
+ "anyOf": [],
1868
+ "params": [
1869
+ {
1870
+ "name": "url",
1871
+ "type": "string",
1872
+ "required": true,
1873
+ "anyOf": false
1874
+ },
1875
+ {
1876
+ "name": "handle",
1877
+ "type": "string",
1878
+ "required": false,
1879
+ "anyOf": false
1880
+ }
1881
+ ]
1882
+ },
1883
+ {
1884
+ "platform": "twitter",
1885
+ "action": "community",
1886
+ "resource": "community",
1887
+ "version": "v1",
1888
+ "method": "GET",
1889
+ "path": "/v1/twitter/community",
1890
+ "toolName": "v1_twitter_community",
1891
+ "summary": "Get an X/Twitter Community by id or URL.",
1892
+ "cost": 1,
1893
+ "live": false,
1894
+ "anyOf": [
1895
+ [
1896
+ "id",
1897
+ "url"
1898
+ ]
1899
+ ],
1900
+ "params": [
1901
+ {
1902
+ "name": "id",
1903
+ "type": "string",
1904
+ "required": false,
1905
+ "anyOf": true
1906
+ },
1907
+ {
1908
+ "name": "url",
1909
+ "type": "string",
1910
+ "required": false,
1911
+ "anyOf": true
1912
+ },
1913
+ {
1914
+ "name": "handle",
1915
+ "type": "string",
1916
+ "required": false,
1917
+ "anyOf": false
1918
+ }
1919
+ ]
1920
+ },
1921
+ {
1922
+ "platform": "twitter",
1923
+ "action": "community-tweets",
1924
+ "resource": "community/tweets",
1925
+ "version": "v1",
1926
+ "method": "GET",
1927
+ "path": "/v1/twitter/community/tweets",
1928
+ "toolName": "v1_twitter_community_tweets",
1929
+ "summary": "Get the tweets of an X/Twitter Community (paginated via cursor).",
1930
+ "cost": 1,
1931
+ "live": false,
1932
+ "anyOf": [
1933
+ [
1934
+ "id",
1935
+ "url"
1936
+ ]
1937
+ ],
1938
+ "params": [
1939
+ {
1940
+ "name": "id",
1941
+ "type": "string",
1942
+ "required": false,
1943
+ "anyOf": true
1944
+ },
1945
+ {
1946
+ "name": "url",
1947
+ "type": "string",
1948
+ "required": false,
1949
+ "anyOf": true
1950
+ },
1951
+ {
1952
+ "name": "handle",
1953
+ "type": "string",
1954
+ "required": false,
1955
+ "anyOf": false
1956
+ }
1957
+ ]
1958
+ },
1959
+ {
1960
+ "platform": "spotify",
1961
+ "action": "artist",
1962
+ "resource": "artist",
1963
+ "version": "v1",
1964
+ "method": "GET",
1965
+ "path": "/v1/spotify/artist",
1966
+ "toolName": "v1_spotify_artist",
1967
+ "summary": "Get a Spotify artist (profile, stats, discography) by URL or id.",
1968
+ "cost": 1,
1969
+ "live": true,
1970
+ "anyOf": [
1971
+ [
1972
+ "url",
1973
+ "id"
1974
+ ]
1975
+ ],
1976
+ "params": [
1977
+ {
1978
+ "name": "url",
1979
+ "type": "string",
1980
+ "required": false,
1981
+ "anyOf": true
1982
+ },
1983
+ {
1984
+ "name": "id",
1985
+ "type": "string",
1986
+ "required": false,
1987
+ "anyOf": true
1988
+ }
1989
+ ]
1990
+ },
1991
+ {
1992
+ "platform": "spotify",
1993
+ "action": "track",
1994
+ "resource": "track",
1995
+ "version": "v1",
1996
+ "method": "GET",
1997
+ "path": "/v1/spotify/track",
1998
+ "toolName": "v1_spotify_track",
1999
+ "summary": "Get a single Spotify track by URL or id.",
2000
+ "cost": 1,
2001
+ "live": true,
2002
+ "anyOf": [
2003
+ [
2004
+ "url",
2005
+ "id"
2006
+ ]
2007
+ ],
2008
+ "params": [
2009
+ {
2010
+ "name": "url",
2011
+ "type": "string",
2012
+ "required": false,
2013
+ "anyOf": true
2014
+ },
2015
+ {
2016
+ "name": "id",
2017
+ "type": "string",
2018
+ "required": false,
2019
+ "anyOf": true
2020
+ }
2021
+ ]
2022
+ },
2023
+ {
2024
+ "platform": "spotify",
2025
+ "action": "album",
2026
+ "resource": "album",
2027
+ "version": "v1",
2028
+ "method": "GET",
2029
+ "path": "/v1/spotify/album",
2030
+ "toolName": "v1_spotify_album",
2031
+ "summary": "Get a Spotify album (with tracks) by URL or id.",
2032
+ "cost": 1,
2033
+ "live": true,
2034
+ "anyOf": [
2035
+ [
2036
+ "url",
2037
+ "id"
2038
+ ]
2039
+ ],
2040
+ "params": [
2041
+ {
2042
+ "name": "url",
2043
+ "type": "string",
2044
+ "required": false,
2045
+ "anyOf": true
2046
+ },
2047
+ {
2048
+ "name": "id",
2049
+ "type": "string",
2050
+ "required": false,
2051
+ "anyOf": true
2052
+ }
2053
+ ]
2054
+ },
2055
+ {
2056
+ "platform": "spotify",
2057
+ "action": "search",
2058
+ "resource": "search",
2059
+ "version": "v1",
2060
+ "method": "GET",
2061
+ "path": "/v1/spotify/search",
2062
+ "toolName": "v1_spotify_search",
2063
+ "summary": "Search Spotify across artists, albums, tracks, playlists, podcasts and episodes.",
2064
+ "cost": 1,
2065
+ "live": true,
2066
+ "anyOf": [],
2067
+ "params": [
2068
+ {
2069
+ "name": "query",
2070
+ "type": "string",
2071
+ "required": true,
2072
+ "anyOf": false
2073
+ },
2074
+ {
2075
+ "name": "url",
2076
+ "type": "string",
2077
+ "required": false,
2078
+ "anyOf": false
2079
+ }
2080
+ ]
2081
+ },
2082
+ {
2083
+ "platform": "spotify",
2084
+ "action": "podcast",
2085
+ "resource": "podcast",
2086
+ "version": "v1",
2087
+ "method": "GET",
2088
+ "path": "/v1/spotify/podcast",
2089
+ "toolName": "v1_spotify_podcast",
2090
+ "summary": "Get a Spotify podcast/show's metadata by URL or id.",
2091
+ "cost": 1,
2092
+ "live": true,
2093
+ "anyOf": [
2094
+ [
2095
+ "url",
2096
+ "id"
2097
+ ]
2098
+ ],
2099
+ "params": [
2100
+ {
2101
+ "name": "url",
2102
+ "type": "string",
2103
+ "required": false,
2104
+ "anyOf": true
2105
+ },
2106
+ {
2107
+ "name": "id",
2108
+ "type": "string",
2109
+ "required": false,
2110
+ "anyOf": true
2111
+ }
2112
+ ]
2113
+ },
2114
+ {
2115
+ "platform": "spotify",
2116
+ "action": "podcast-episodes",
2117
+ "resource": "podcast/episodes",
2118
+ "version": "v1",
2119
+ "method": "GET",
2120
+ "path": "/v1/spotify/podcast/episodes",
2121
+ "toolName": "v1_spotify_podcast_episodes",
2122
+ "summary": "Get a Spotify podcast's episodes by URL or id (paginate via ?cursor=).",
2123
+ "cost": 1,
2124
+ "live": true,
2125
+ "anyOf": [
2126
+ [
2127
+ "url",
2128
+ "id"
2129
+ ]
2130
+ ],
2131
+ "params": [
2132
+ {
2133
+ "name": "url",
2134
+ "type": "string",
2135
+ "required": false,
2136
+ "anyOf": true
2137
+ },
2138
+ {
2139
+ "name": "id",
2140
+ "type": "string",
2141
+ "required": false,
2142
+ "anyOf": true
2143
+ }
2144
+ ]
2145
+ },
2146
+ {
2147
+ "platform": "applemusic",
2148
+ "action": "artist",
2149
+ "resource": "artist",
2150
+ "version": "v1",
2151
+ "method": "GET",
2152
+ "path": "/v1/applemusic/artist",
2153
+ "toolName": "v1_applemusic_artist",
2154
+ "summary": "Get an Apple Music artist (profile, top songs, discography) by URL or id.",
2155
+ "cost": 1,
2156
+ "live": true,
2157
+ "anyOf": [
2158
+ [
2159
+ "url",
2160
+ "id"
2161
+ ]
2162
+ ],
2163
+ "params": [
2164
+ {
2165
+ "name": "url",
2166
+ "type": "string",
2167
+ "required": false,
2168
+ "anyOf": true
2169
+ },
2170
+ {
2171
+ "name": "id",
2172
+ "type": "string",
2173
+ "required": false,
2174
+ "anyOf": true
2175
+ }
2176
+ ]
2177
+ },
2178
+ {
2179
+ "platform": "applemusic",
2180
+ "action": "album",
2181
+ "resource": "album",
2182
+ "version": "v1",
2183
+ "method": "GET",
2184
+ "path": "/v1/applemusic/album",
2185
+ "toolName": "v1_applemusic_album",
2186
+ "summary": "Get an Apple Music album (metadata + full tracklist) by URL or id.",
2187
+ "cost": 1,
2188
+ "live": true,
2189
+ "anyOf": [
2190
+ [
2191
+ "url",
2192
+ "id"
2193
+ ]
2194
+ ],
2195
+ "params": [
2196
+ {
2197
+ "name": "url",
2198
+ "type": "string",
2199
+ "required": false,
2200
+ "anyOf": true
2201
+ },
2202
+ {
2203
+ "name": "id",
2204
+ "type": "string",
2205
+ "required": false,
2206
+ "anyOf": true
2207
+ }
2208
+ ]
2209
+ },
2210
+ {
2211
+ "platform": "applemusic",
2212
+ "action": "track",
2213
+ "resource": "track",
2214
+ "version": "v1",
2215
+ "method": "GET",
2216
+ "path": "/v1/applemusic/track",
2217
+ "toolName": "v1_applemusic_track",
2218
+ "summary": "Get a single Apple Music song by URL or id.",
2219
+ "cost": 1,
2220
+ "live": true,
2221
+ "anyOf": [
2222
+ [
2223
+ "url",
2224
+ "id"
2225
+ ]
2226
+ ],
2227
+ "params": [
2228
+ {
2229
+ "name": "url",
2230
+ "type": "string",
2231
+ "required": false,
2232
+ "anyOf": true
2233
+ },
2234
+ {
2235
+ "name": "id",
2236
+ "type": "string",
2237
+ "required": false,
2238
+ "anyOf": true
2239
+ }
2240
+ ]
2241
+ },
2242
+ {
2243
+ "platform": "applemusic",
2244
+ "action": "search",
2245
+ "resource": "search",
2246
+ "version": "v1",
2247
+ "method": "GET",
2248
+ "path": "/v1/applemusic/search",
2249
+ "toolName": "v1_applemusic_search",
2250
+ "summary": "Search the Apple Music catalog (songs/albums/artists) by query.",
2251
+ "cost": 1,
2252
+ "live": true,
2253
+ "anyOf": [],
2254
+ "params": [
2255
+ {
2256
+ "name": "query",
2257
+ "type": "string",
2258
+ "required": true,
2259
+ "anyOf": false
2260
+ },
2261
+ {
2262
+ "name": "url",
2263
+ "type": "string",
2264
+ "required": false,
2265
+ "anyOf": false
2266
+ },
2267
+ {
2268
+ "name": "id",
2269
+ "type": "string",
2270
+ "required": false,
2271
+ "anyOf": false
2272
+ }
2273
+ ]
2274
+ },
2275
+ {
2276
+ "platform": "kleinanzeigen",
2277
+ "action": "search",
2278
+ "resource": "search",
2279
+ "version": "v1",
2280
+ "method": "GET",
2281
+ "path": "/v1/kleinanzeigen/search",
2282
+ "toolName": "v1_kleinanzeigen_search",
2283
+ "summary": "Search Kleinanzeigen listings by keyword and/or category+location, with filters (price, ad/poster type, sort, distance) and pagination. Provide a `query`, a `categoryId`, or a `locationId` (any combination).",
2284
+ "cost": 1,
2285
+ "live": true,
2286
+ "anyOf": [
2287
+ [
2288
+ "query",
2289
+ "categoryId",
2290
+ "locationId"
2291
+ ]
2292
+ ],
2293
+ "params": [
2294
+ {
2295
+ "name": "query",
2296
+ "type": "string",
2297
+ "required": false,
2298
+ "anyOf": true,
2299
+ "description": "Free-text keyword. Optional when a categoryId/locationId is given.",
2300
+ "example": "fahrrad"
2301
+ },
2302
+ {
2303
+ "name": "categoryId",
2304
+ "type": "string",
2305
+ "required": false,
2306
+ "anyOf": true,
2307
+ "description": "Kleinanzeigen category id (e.g. 203 = Mietwohnungen). Browse without a keyword.",
2308
+ "example": "203"
2309
+ },
2310
+ {
2311
+ "name": "locationId",
2312
+ "type": "string",
2313
+ "required": false,
2314
+ "anyOf": true,
2315
+ "description": "Location id from the /locations endpoint (city or ZIP). Anchors the search + `distance`.",
2316
+ "example": "996"
2317
+ },
2318
+ {
2319
+ "name": "minPrice",
2320
+ "type": "string",
2321
+ "required": false,
2322
+ "anyOf": false,
2323
+ "description": "Minimum price in EUR.",
2324
+ "example": "50"
2325
+ },
2326
+ {
2327
+ "name": "maxPrice",
2328
+ "type": "string",
2329
+ "required": false,
2330
+ "anyOf": false,
2331
+ "description": "Maximum price in EUR.",
2332
+ "example": "1500"
2333
+ },
2334
+ {
2335
+ "name": "adType",
2336
+ "type": "string",
2337
+ "required": false,
2338
+ "anyOf": false,
2339
+ "description": "Listing type.",
2340
+ "enum": [
2341
+ "OFFERED",
2342
+ "WANTED"
2343
+ ]
2344
+ },
2345
+ {
2346
+ "name": "posterType",
2347
+ "type": "string",
2348
+ "required": false,
2349
+ "anyOf": false,
2350
+ "description": "Seller type.",
2351
+ "enum": [
2352
+ "PRIVATE",
2353
+ "COMMERCIAL"
2354
+ ]
2355
+ },
2356
+ {
2357
+ "name": "sortType",
2358
+ "type": "string",
2359
+ "required": false,
2360
+ "anyOf": false,
2361
+ "description": "Result ordering (default: newest first).",
2362
+ "enum": [
2363
+ "PRICE_ASCENDING",
2364
+ "PRICE_DESCENDING",
2365
+ "DATE_DESCENDING",
2366
+ "DISTANCE"
2367
+ ]
2368
+ },
2369
+ {
2370
+ "name": "distance",
2371
+ "type": "string",
2372
+ "required": false,
2373
+ "anyOf": false,
2374
+ "description": "Search radius in km around locationId.",
2375
+ "example": "20"
2376
+ },
2377
+ {
2378
+ "name": "pictureRequired",
2379
+ "type": "string",
2380
+ "required": false,
2381
+ "anyOf": false,
2382
+ "description": "Only listings with at least one photo.",
2383
+ "enum": [
2384
+ "true",
2385
+ "false"
2386
+ ]
2387
+ },
2388
+ {
2389
+ "name": "page",
2390
+ "type": "string",
2391
+ "required": false,
2392
+ "anyOf": false,
2393
+ "description": "0-based page index for pagination.",
2394
+ "example": "0"
2395
+ },
2396
+ {
2397
+ "name": "size",
2398
+ "type": "string",
2399
+ "required": false,
2400
+ "anyOf": false,
2401
+ "description": "Results per page (1\u201341, default 25).",
2402
+ "example": "25"
2403
+ }
2404
+ ]
2405
+ },
2406
+ {
2407
+ "platform": "kleinanzeigen",
2408
+ "action": "ad",
2409
+ "resource": "ad",
2410
+ "version": "v1",
2411
+ "method": "GET",
2412
+ "path": "/v1/kleinanzeigen/ad",
2413
+ "toolName": "v1_kleinanzeigen_ad",
2414
+ "summary": "Get a single Kleinanzeigen listing (full detail: price, attributes, images, seller) by id or URL.",
2415
+ "cost": 1,
2416
+ "live": true,
2417
+ "anyOf": [
2418
+ [
2419
+ "id",
2420
+ "url"
2421
+ ]
2422
+ ],
2423
+ "params": [
2424
+ {
2425
+ "name": "id",
2426
+ "type": "string",
2427
+ "required": false,
2428
+ "anyOf": true,
2429
+ "description": "Numeric ad id.",
2430
+ "example": "3460253912"
2431
+ },
2432
+ {
2433
+ "name": "url",
2434
+ "type": "string",
2435
+ "required": false,
2436
+ "anyOf": true,
2437
+ "description": "Full listing URL (web or API) \u2014 the ad id is extracted from it.",
2438
+ "example": "https://www.kleinanzeigen.de/s-anzeige/\u2026/3460253912-203-996"
2439
+ }
2440
+ ]
2441
+ },
2442
+ {
2443
+ "platform": "kleinanzeigen",
2444
+ "action": "locations",
2445
+ "resource": "locations",
2446
+ "version": "v1",
2447
+ "method": "GET",
2448
+ "path": "/v1/kleinanzeigen/locations",
2449
+ "toolName": "v1_kleinanzeigen_locations",
2450
+ "summary": "Resolve a place name (city / ZIP) to Kleinanzeigen locationId(s) for the search `locationId` filter.",
2451
+ "cost": 1,
2452
+ "live": true,
2453
+ "anyOf": [],
2454
+ "params": [
2455
+ {
2456
+ "name": "query",
2457
+ "type": "string",
2458
+ "required": true,
2459
+ "anyOf": false,
2460
+ "description": "City name or ZIP code to resolve.",
2461
+ "example": "51063"
2462
+ },
2463
+ {
2464
+ "name": "depth",
2465
+ "type": "string",
2466
+ "required": false,
2467
+ "anyOf": false,
2468
+ "description": "How many sub-location levels to include (0 = the matched locations only).",
2469
+ "example": "0"
2470
+ }
2471
+ ]
2472
+ },
2473
+ {
2474
+ "platform": "11880",
2475
+ "action": "search",
2476
+ "resource": "search",
2477
+ "version": "v1",
2478
+ "method": "GET",
2479
+ "path": "/v1/11880/search",
2480
+ "toolName": "v1_11880_search",
2481
+ "summary": "Search 11880.com business listings for a term in a location (paginated).",
2482
+ "cost": 1,
2483
+ "live": true,
2484
+ "anyOf": [],
2485
+ "params": [
2486
+ {
2487
+ "name": "query",
2488
+ "type": "string",
2489
+ "required": true,
2490
+ "anyOf": false
2491
+ },
2492
+ {
2493
+ "name": "location",
2494
+ "type": "string",
2495
+ "required": false,
2496
+ "anyOf": false
2497
+ },
2498
+ {
2499
+ "name": "page",
2500
+ "type": "string",
2501
+ "required": false,
2502
+ "anyOf": false
2503
+ },
2504
+ {
2505
+ "name": "sort",
2506
+ "type": "string",
2507
+ "required": false,
2508
+ "anyOf": false
2509
+ },
2510
+ {
2511
+ "name": "url",
2512
+ "type": "string",
2513
+ "required": false,
2514
+ "anyOf": false
2515
+ }
2516
+ ]
2517
+ },
2518
+ {
2519
+ "platform": "11880",
2520
+ "action": "detail",
2521
+ "resource": "detail",
2522
+ "version": "v1",
2523
+ "method": "GET",
2524
+ "path": "/v1/11880/detail",
2525
+ "toolName": "v1_11880_detail",
2526
+ "summary": "Fetch a single 11880.com business detail page by its URL.",
2527
+ "cost": 1,
2528
+ "live": true,
2529
+ "anyOf": [],
2530
+ "params": [
2531
+ {
2532
+ "name": "url",
2533
+ "type": "string",
2534
+ "required": true,
2535
+ "anyOf": false
2536
+ },
2537
+ {
2538
+ "name": "query",
2539
+ "type": "string",
2540
+ "required": false,
2541
+ "anyOf": false
2542
+ },
2543
+ {
2544
+ "name": "location",
2545
+ "type": "string",
2546
+ "required": false,
2547
+ "anyOf": false
2548
+ },
2549
+ {
2550
+ "name": "page",
2551
+ "type": "string",
2552
+ "required": false,
2553
+ "anyOf": false
2554
+ },
2555
+ {
2556
+ "name": "sort",
2557
+ "type": "string",
2558
+ "required": false,
2559
+ "anyOf": false
2560
+ }
2561
+ ]
2562
+ },
2563
+ {
2564
+ "platform": "gelbeseiten",
2565
+ "action": "search",
2566
+ "resource": "search",
2567
+ "version": "v1",
2568
+ "method": "GET",
2569
+ "path": "/v1/gelbeseiten/search",
2570
+ "toolName": "v1_gelbeseiten_search",
2571
+ "summary": "Search Gelbe Seiten businesses by keyword (query) and place (location), with radius / sort / open-now filters and page-based pagination.",
2572
+ "cost": 1,
2573
+ "live": true,
2574
+ "anyOf": [],
2575
+ "params": [
2576
+ {
2577
+ "name": "query",
2578
+ "type": "string",
2579
+ "required": true,
2580
+ "anyOf": false
2581
+ },
2582
+ {
2583
+ "name": "location",
2584
+ "type": "string",
2585
+ "required": false,
2586
+ "anyOf": false
2587
+ },
2588
+ {
2589
+ "name": "radius",
2590
+ "type": "string",
2591
+ "required": false,
2592
+ "anyOf": false
2593
+ },
2594
+ {
2595
+ "name": "sort",
2596
+ "type": "string",
2597
+ "required": false,
2598
+ "anyOf": false
2599
+ },
2600
+ {
2601
+ "name": "open_now",
2602
+ "type": "string",
2603
+ "required": false,
2604
+ "anyOf": false
2605
+ },
2606
+ {
2607
+ "name": "page",
2608
+ "type": "string",
2609
+ "required": false,
2610
+ "anyOf": false
2611
+ },
2612
+ {
2613
+ "name": "id",
2614
+ "type": "string",
2615
+ "required": false,
2616
+ "anyOf": false
2617
+ },
2618
+ {
2619
+ "name": "url",
2620
+ "type": "string",
2621
+ "required": false,
2622
+ "anyOf": false
2623
+ }
2624
+ ]
2625
+ },
2626
+ {
2627
+ "platform": "gelbeseiten",
2628
+ "action": "listing",
2629
+ "resource": "listing",
2630
+ "version": "v1",
2631
+ "method": "GET",
2632
+ "path": "/v1/gelbeseiten/listing",
2633
+ "toolName": "v1_gelbeseiten_listing",
2634
+ "summary": "Get one Gelbe Seiten business listing (full detail: address, phone, website, rating, opening hours) by numeric id or URL.",
2635
+ "cost": 1,
2636
+ "live": true,
2637
+ "anyOf": [
2638
+ [
2639
+ "id",
2640
+ "url"
2641
+ ]
2642
+ ],
2643
+ "params": [
2644
+ {
2645
+ "name": "id",
2646
+ "type": "string",
2647
+ "required": false,
2648
+ "anyOf": true
2649
+ },
2650
+ {
2651
+ "name": "url",
2652
+ "type": "string",
2653
+ "required": false,
2654
+ "anyOf": true
2655
+ },
2656
+ {
2657
+ "name": "query",
2658
+ "type": "string",
2659
+ "required": false,
2660
+ "anyOf": false
2661
+ },
2662
+ {
2663
+ "name": "location",
2664
+ "type": "string",
2665
+ "required": false,
2666
+ "anyOf": false
2667
+ },
2668
+ {
2669
+ "name": "radius",
2670
+ "type": "string",
2671
+ "required": false,
2672
+ "anyOf": false
2673
+ },
2674
+ {
2675
+ "name": "sort",
2676
+ "type": "string",
2677
+ "required": false,
2678
+ "anyOf": false
2679
+ },
2680
+ {
2681
+ "name": "open_now",
2682
+ "type": "string",
2683
+ "required": false,
2684
+ "anyOf": false
2685
+ },
2686
+ {
2687
+ "name": "page",
2688
+ "type": "string",
2689
+ "required": false,
2690
+ "anyOf": false
2691
+ }
2692
+ ]
2693
+ },
2694
+ {
2695
+ "platform": "immobilienscout24",
2696
+ "action": "search",
2697
+ "resource": "search",
2698
+ "version": "v1",
2699
+ "method": "GET",
2700
+ "path": "/v1/immobilienscout24/search",
2701
+ "toolName": "v1_immobilienscout24_search",
2702
+ "summary": "Search ImmobilienScout24 real-estate listings by location (place name, region path, or lat/lon radius) with filters (type, price, rooms, living space, courtage-free, new-build, full-text) and pagination.",
2703
+ "cost": 1,
2704
+ "live": true,
2705
+ "anyOf": [
2706
+ [
2707
+ "location",
2708
+ "geocodes",
2709
+ "geocoordinates"
2710
+ ]
2711
+ ],
2712
+ "params": [
2713
+ {
2714
+ "name": "location",
2715
+ "type": "string",
2716
+ "required": false,
2717
+ "anyOf": true
2718
+ },
2719
+ {
2720
+ "name": "geocodes",
2721
+ "type": "string",
2722
+ "required": false,
2723
+ "anyOf": true
2724
+ },
2725
+ {
2726
+ "name": "geocoordinates",
2727
+ "type": "string",
2728
+ "required": false,
2729
+ "anyOf": true
2730
+ },
2731
+ {
2732
+ "name": "real_estate_type",
2733
+ "type": "string",
2734
+ "required": false,
2735
+ "anyOf": false
2736
+ },
2737
+ {
2738
+ "name": "price_min",
2739
+ "type": "string",
2740
+ "required": false,
2741
+ "anyOf": false
2742
+ },
2743
+ {
2744
+ "name": "price_max",
2745
+ "type": "string",
2746
+ "required": false,
2747
+ "anyOf": false
2748
+ },
2749
+ {
2750
+ "name": "rooms_min",
2751
+ "type": "string",
2752
+ "required": false,
2753
+ "anyOf": false
2754
+ },
2755
+ {
2756
+ "name": "rooms_max",
2757
+ "type": "string",
2758
+ "required": false,
2759
+ "anyOf": false
2760
+ },
2761
+ {
2762
+ "name": "living_space_min",
2763
+ "type": "string",
2764
+ "required": false,
2765
+ "anyOf": false
2766
+ },
2767
+ {
2768
+ "name": "living_space_max",
2769
+ "type": "string",
2770
+ "required": false,
2771
+ "anyOf": false
2772
+ },
2773
+ {
2774
+ "name": "free_of_courtage",
2775
+ "type": "string",
2776
+ "required": false,
2777
+ "anyOf": false
2778
+ },
2779
+ {
2780
+ "name": "newbuilding",
2781
+ "type": "string",
2782
+ "required": false,
2783
+ "anyOf": false
2784
+ },
2785
+ {
2786
+ "name": "fulltext",
2787
+ "type": "string",
2788
+ "required": false,
2789
+ "anyOf": false
2790
+ },
2791
+ {
2792
+ "name": "sort",
2793
+ "type": "string",
2794
+ "required": false,
2795
+ "anyOf": false
2796
+ },
2797
+ {
2798
+ "name": "page",
2799
+ "type": "string",
2800
+ "required": false,
2801
+ "anyOf": false
2802
+ },
2803
+ {
2804
+ "name": "page_size",
2805
+ "type": "string",
2806
+ "required": false,
2807
+ "anyOf": false
2808
+ }
2809
+ ]
2810
+ },
2811
+ {
2812
+ "platform": "immobilienscout24",
2813
+ "action": "expose",
2814
+ "resource": "expose",
2815
+ "version": "v1",
2816
+ "method": "GET",
2817
+ "path": "/v1/immobilienscout24/expose",
2818
+ "toolName": "v1_immobilienscout24_expose",
2819
+ "summary": "Get a single ImmobilienScout24 listing (full detail: price, criteria, images, agent, texts) by id or URL.",
2820
+ "cost": 1,
2821
+ "live": true,
2822
+ "anyOf": [
2823
+ [
2824
+ "id",
2825
+ "url"
2826
+ ]
2827
+ ],
2828
+ "params": [
2829
+ {
2830
+ "name": "id",
2831
+ "type": "string",
2832
+ "required": false,
2833
+ "anyOf": true
2834
+ },
2835
+ {
2836
+ "name": "url",
2837
+ "type": "string",
2838
+ "required": false,
2839
+ "anyOf": true
2840
+ }
2841
+ ]
2842
+ }
2843
+ ];
2844
+
2845
+ // ../../packages/api-client/src/endpoints.js
2846
+ var platforms = [...new Set(endpoints.map((e) => e.platform))];
2847
+ function endpointsForPlatform(platform) {
2848
+ return endpoints.filter((e) => e.platform === platform);
2849
+ }
2850
+
2851
+ // src/config.js
2852
+ import os from "node:os";
2853
+ import path from "node:path";
2854
+ import fs from "node:fs";
2855
+ import Conf from "conf";
2856
+ var CONFIG_DIR = path.join(os.homedir(), ".config", "scraper-api");
2857
+ var instance = null;
2858
+ function harden(store) {
2859
+ try {
2860
+ fs.chmodSync(CONFIG_DIR, 448);
2861
+ } catch {
2862
+ }
2863
+ try {
2864
+ fs.chmodSync(store.path, 384);
2865
+ } catch {
2866
+ }
2867
+ }
2868
+ function getConfig() {
2869
+ if (instance) return instance;
2870
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
2871
+ const store = new Conf({ projectName: "scraper-api", configName: "config", cwd: CONFIG_DIR });
2872
+ harden(store);
2873
+ instance = store;
2874
+ return store;
2875
+ }
2876
+ function configSet(key, value) {
2877
+ const store = getConfig();
2878
+ store.set(key, value);
2879
+ harden(store);
2880
+ }
2881
+ function configGet(key) {
2882
+ return getConfig().get(key);
2883
+ }
2884
+ function configDelete(key) {
2885
+ const store = getConfig();
2886
+ store.delete(key);
2887
+ harden(store);
2888
+ }
2889
+ function configPath() {
2890
+ return getConfig().path;
2891
+ }
2892
+ var KEYS = {
2893
+ apiKey: "api-key",
2894
+ email: "email",
2895
+ authDomain: "auth-domain",
2896
+ clientId: "client-id",
2897
+ audience: "audience"
2898
+ };
2899
+
2900
+ // src/auth.js
2901
+ function resolveApiKey(flagKey) {
2902
+ if (flagKey) return flagKey;
2903
+ const stored = configGet(KEYS.apiKey);
2904
+ if (stored) return stored;
2905
+ if (process.env.SCRAPER_API_KEY) return process.env.SCRAPER_API_KEY;
2906
+ return null;
2907
+ }
2908
+ function requireApiKey(flagKey) {
2909
+ const key = resolveApiKey(flagKey);
2910
+ if (!key) {
2911
+ throw new Error(
2912
+ "No API key found. Run `scraper-api login` (OAuth) or `scraper-api auth login --api-key <key>`, or set the SCRAPER_API_KEY environment variable."
2913
+ );
2914
+ }
2915
+ return key;
2916
+ }
2917
+ var DEFAULT_AUTH_DOMAIN = "auth.scraper-api.com";
2918
+ var DEFAULT_CLIENT_ID = "jf81Z8XIF5SUDKJPMLJzCEa3vG1vzzgo";
2919
+ function resolveAuthSettings() {
2920
+ const authDomain = configGet(KEYS.authDomain) || process.env.SCRAPER_API_AUTH_DOMAIN || process.env.AUTH0_DOMAIN || DEFAULT_AUTH_DOMAIN;
2921
+ const clientId = configGet(KEYS.clientId) || process.env.SCRAPER_API_CLIENT_ID || process.env.AUTH0_CLIENT_ID || DEFAULT_CLIENT_ID;
2922
+ const audience = configGet(KEYS.audience) || process.env.SCRAPER_API_AUDIENCE || process.env.AUTH0_AUDIENCE || "https://api.scraper-api.com";
2923
+ return { authDomain, clientId, audience };
2924
+ }
2925
+
2926
+ // src/api-client.js
2927
+ var DEFAULT_BASE = "https://api.scraper-api.com";
2928
+ var TIMEOUT_MS = 6e4;
2929
+ function apiBase() {
2930
+ return (process.env.SCRAPER_API_BASE || DEFAULT_BASE).replace(/\/$/, "");
2931
+ }
2932
+ function buildError(status, body) {
2933
+ const detail = typeof body === "string" ? body : body && (body.message || body.error) || JSON.stringify(body);
2934
+ const err = new Error(`API ${status}: ${detail}`);
2935
+ err.status = status;
2936
+ err.body = body;
2937
+ return err;
2938
+ }
2939
+ async function parse(res) {
2940
+ const text2 = await res.text();
2941
+ if (!text2) return null;
2942
+ try {
2943
+ return JSON.parse(text2);
2944
+ } catch {
2945
+ return text2;
2946
+ }
2947
+ }
2948
+ async function withTimeout(run2) {
2949
+ const controller = new AbortController();
2950
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
2951
+ try {
2952
+ return await run2(controller.signal);
2953
+ } catch (err) {
2954
+ if (err?.name === "AbortError") throw new Error(`Request timed out after ${TIMEOUT_MS / 1e3}s`);
2955
+ throw err;
2956
+ } finally {
2957
+ clearTimeout(timer);
2958
+ }
2959
+ }
2960
+ function buildUrl(path3, params = {}) {
2961
+ const url = new URL(apiBase() + (path3.startsWith("/") ? path3 : `/${path3}`));
2962
+ for (const [key, value] of Object.entries(params)) {
2963
+ if (value === void 0 || value === null || value === "") continue;
2964
+ url.searchParams.set(key, String(value));
2965
+ }
2966
+ return url.toString();
2967
+ }
2968
+ async function apiGet(path3, params, apiKey) {
2969
+ return withTimeout(async (signal) => {
2970
+ const res = await fetch(buildUrl(path3, params), {
2971
+ method: "GET",
2972
+ headers: { "x-api-key": apiKey, accept: "application/json" },
2973
+ signal
2974
+ });
2975
+ const body = await parse(res);
2976
+ if (!res.ok) throw buildError(res.status, body);
2977
+ return body;
2978
+ });
2979
+ }
2980
+ async function bearerPost(path3, accessToken, payload) {
2981
+ return withTimeout(async (signal) => {
2982
+ const res = await fetch(buildUrl(path3), {
2983
+ method: "POST",
2984
+ headers: {
2985
+ authorization: `Bearer ${accessToken}`,
2986
+ accept: "application/json",
2987
+ "content-type": "application/json"
2988
+ },
2989
+ body: JSON.stringify(payload ?? {}),
2990
+ signal
2991
+ });
2992
+ const body = await parse(res);
2993
+ if (!res.ok) throw buildError(res.status, body);
2994
+ return body;
2995
+ });
2996
+ }
2997
+
2998
+ // src/output.js
2999
+ import fs2 from "node:fs";
3000
+ import Table from "cli-table3";
3001
+ function toRows(data) {
3002
+ if (Array.isArray(data)) return data;
3003
+ if (data && typeof data === "object") {
3004
+ const arrayField = Object.values(data).find((v) => Array.isArray(v));
3005
+ if (arrayField) return arrayField;
3006
+ return [data];
3007
+ }
3008
+ return [{ value: data }];
3009
+ }
3010
+ function columns(rows) {
3011
+ const set = /* @__PURE__ */ new Set();
3012
+ for (const row of rows) {
3013
+ if (row && typeof row === "object" && !Array.isArray(row)) {
3014
+ for (const key of Object.keys(row)) set.add(key);
3015
+ } else {
3016
+ set.add("value");
3017
+ }
3018
+ }
3019
+ return [...set];
3020
+ }
3021
+ function cell(value) {
3022
+ if (value === null || value === void 0) return "";
3023
+ if (typeof value === "object") return JSON.stringify(value);
3024
+ return String(value);
3025
+ }
3026
+ function asTable(rows) {
3027
+ const cols = columns(rows);
3028
+ const table = new Table({ head: cols });
3029
+ for (const row of rows) {
3030
+ if (row && typeof row === "object" && !Array.isArray(row)) {
3031
+ table.push(cols.map((c) => cell(row[c])));
3032
+ } else {
3033
+ table.push([cell(row)]);
3034
+ }
3035
+ }
3036
+ return table.toString();
3037
+ }
3038
+ function asCsv(rows) {
3039
+ const cols = columns(rows);
3040
+ const escape = (v) => {
3041
+ const s = cell(v);
3042
+ return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
3043
+ };
3044
+ const lines = [cols.join(",")];
3045
+ for (const row of rows) {
3046
+ if (row && typeof row === "object" && !Array.isArray(row)) {
3047
+ lines.push(cols.map((c) => escape(row[c])).join(","));
3048
+ } else {
3049
+ lines.push(escape(row));
3050
+ }
3051
+ }
3052
+ return lines.join("\n");
3053
+ }
3054
+ function asMarkdown(rows) {
3055
+ const cols = columns(rows);
3056
+ const escape = (v) => cell(v).replace(/\|/g, "\\|").replace(/\n/g, " ");
3057
+ const header = `| ${cols.join(" | ")} |`;
3058
+ const sep = `| ${cols.map(() => "---").join(" | ")} |`;
3059
+ const body = rows.map((row) => {
3060
+ if (row && typeof row === "object" && !Array.isArray(row)) {
3061
+ return `| ${cols.map((c) => escape(row[c])).join(" | ")} |`;
3062
+ }
3063
+ return `| ${escape(row)} |`;
3064
+ });
3065
+ return [header, sep, ...body].join("\n");
3066
+ }
3067
+ function format(data, { format: fmt = "json", pretty = false } = {}) {
3068
+ switch (fmt) {
3069
+ case "table":
3070
+ return asTable(toRows(data));
3071
+ case "csv":
3072
+ return asCsv(toRows(data));
3073
+ case "markdown":
3074
+ return asMarkdown(toRows(data));
3075
+ case "json":
3076
+ default:
3077
+ return JSON.stringify(data, null, pretty ? 2 : 0);
3078
+ }
3079
+ }
3080
+ function emit(data, opts = {}) {
3081
+ const text2 = format(data, opts);
3082
+ if (opts.output) {
3083
+ fs2.writeFileSync(opts.output, text2);
3084
+ process.stderr.write(`Saved to ${opts.output}
3085
+ `);
3086
+ return;
3087
+ }
3088
+ process.stdout.write(text2 + "\n");
3089
+ }
3090
+
3091
+ // src/command-registry.js
3092
+ function paramFlag(param) {
3093
+ const bits = [];
3094
+ if (param.required) bits.push(chalk.yellow("required"));
3095
+ else if (param.anyOf) bits.push("any-of");
3096
+ if (param.enum) bits.push(`one of: ${param.enum.join(", ")}`);
3097
+ const suffix = bits.length ? ` (${bits.join("; ")})` : "";
3098
+ return { flag: `--${param.name} <value>`, description: `${param.description ?? param.name}${suffix}` };
3099
+ }
3100
+ function collectParams(endpoint, opts) {
3101
+ const params = {};
3102
+ for (const param of endpoint.params) {
3103
+ const value = opts[param.name];
3104
+ if (value !== void 0 && value !== null && value !== "") params[param.name] = value;
3105
+ }
3106
+ return params;
3107
+ }
3108
+ function validate(endpoint, params) {
3109
+ const problems = [];
3110
+ for (const param of endpoint.params) {
3111
+ if (param.required && params[param.name] === void 0) problems.push(`--${param.name} is required`);
3112
+ }
3113
+ for (const group of endpoint.anyOf ?? []) {
3114
+ if (!group.some((name) => params[name] !== void 0)) {
3115
+ problems.push(`one of ${group.map((n) => `--${n}`).join(", ")} is required`);
3116
+ }
3117
+ }
3118
+ return problems;
3119
+ }
3120
+ function formatOpts(global) {
3121
+ return {
3122
+ format: global.json ? "json" : global.format,
3123
+ pretty: global.pretty,
3124
+ output: global.output
3125
+ };
3126
+ }
3127
+ async function runEndpoint(endpoint, opts, command) {
3128
+ const global = command.optsWithGlobals();
3129
+ const apiKey = requireApiKey(global.apiKey);
3130
+ const params = collectParams(endpoint, opts);
3131
+ const problems = validate(endpoint, params);
3132
+ if (problems.length) throw new Error(`Invalid parameters:
3133
+ - ${problems.join("\n - ")}`);
3134
+ if (endpoint.cost > 1) {
3135
+ process.stderr.write(chalk.yellow(`\u26A0 This endpoint costs ${endpoint.cost} credits per call.
3136
+ `));
3137
+ }
3138
+ if (!endpoint.live) {
3139
+ process.stderr.write(chalk.gray("\u2139 This endpoint is a stub (mock data) \u2014 not yet live.\n"));
3140
+ }
3141
+ const useSpinner = !global.json && !global.output && global.verbose !== false;
3142
+ const spinner2 = useSpinner ? ora({ stream: process.stderr, text: `${endpoint.platform} ${endpoint.action}\u2026` }).start() : null;
3143
+ try {
3144
+ const data = await apiGet(endpoint.path, params, apiKey);
3145
+ spinner2?.stop();
3146
+ if (global.verbose) process.stderr.write(chalk.gray(`GET ${endpoint.path}
3147
+ `));
3148
+ emit(data, formatOpts(global));
3149
+ } catch (err) {
3150
+ spinner2?.stop();
3151
+ throw err;
3152
+ }
3153
+ }
3154
+ function registerEndpointCommands(program) {
3155
+ for (const platform of platforms) {
3156
+ const group = new Command(platform).description(`${platform} endpoints`);
3157
+ for (const endpoint of endpointsForPlatform(platform)) {
3158
+ const sub = new Command(endpoint.action).description(
3159
+ `${endpoint.summary}${endpoint.cost > 1 ? ` [${endpoint.cost} credits]` : ""}${endpoint.live ? "" : " [stub]"}`
3160
+ );
3161
+ for (const param of endpoint.params) {
3162
+ const { flag, description } = paramFlag(param);
3163
+ sub.option(flag, description);
3164
+ }
3165
+ sub.action((opts, cmd) => runEndpoint(endpoint, opts, cmd));
3166
+ group.addCommand(sub);
3167
+ }
3168
+ program.addCommand(group);
3169
+ }
3170
+ return program;
3171
+ }
3172
+
3173
+ // src/commands/login.js
3174
+ import http from "node:http";
3175
+ import crypto from "node:crypto";
3176
+ import os2 from "node:os";
3177
+ import chalk2 from "chalk";
3178
+ import ora2 from "ora";
3179
+ import open from "open";
3180
+ var CALLBACK_TIMEOUT_MS = 18e4;
3181
+ function base64url(buffer) {
3182
+ return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
3183
+ }
3184
+ function pkcePair() {
3185
+ const verifier = base64url(crypto.randomBytes(32));
3186
+ const challenge = base64url(crypto.createHash("sha256").update(verifier).digest());
3187
+ return { verifier, challenge };
3188
+ }
3189
+ var successPage = `<!doctype html><meta charset="utf-8"><title>scraper-api.com</title>
3190
+ <body style="font-family:system-ui;background:#0b0b0f;color:#e6e6e6;display:grid;place-items:center;height:100vh;margin:0">
3191
+ <div style="text-align:center"><h1>\u2713 Signed in</h1><p>You can close this tab and return to the terminal.</p></div>`;
3192
+ var failurePage = `<!doctype html><meta charset="utf-8"><title>scraper-api.com</title>
3193
+ <body style="font-family:system-ui;background:#0b0b0f;color:#e6e6e6;display:grid;place-items:center;height:100vh;margin:0">
3194
+ <div style="text-align:center"><h1>Sign-in failed</h1><p>Return to the terminal for details.</p></div>`;
3195
+ function startCallbackServer(expectedState) {
3196
+ return new Promise((resolvePort, rejectPort) => {
3197
+ let onCode;
3198
+ let onError;
3199
+ const waitForCode = new Promise((res, rej) => {
3200
+ onCode = res;
3201
+ onError = rej;
3202
+ });
3203
+ const server = http.createServer((req, res) => {
3204
+ const url = new URL(req.url, "http://127.0.0.1");
3205
+ if (url.pathname !== "/callback") {
3206
+ res.writeHead(404).end();
3207
+ return;
3208
+ }
3209
+ const error = url.searchParams.get("error");
3210
+ const code = url.searchParams.get("code");
3211
+ const state = url.searchParams.get("state");
3212
+ const finish = (ok, settle) => {
3213
+ res.writeHead(ok ? 200 : 400, { "content-type": "text/html; charset=utf-8" });
3214
+ res.end(ok ? successPage : failurePage);
3215
+ server.close();
3216
+ settle();
3217
+ };
3218
+ if (error) return finish(false, () => onError(new Error(`Authorization failed: ${error}`)));
3219
+ if (state !== expectedState) return finish(false, () => onError(new Error("State mismatch \u2014 aborting for safety.")));
3220
+ if (!code) return finish(false, () => onError(new Error("No authorization code returned.")));
3221
+ finish(true, () => onCode(code));
3222
+ });
3223
+ const timer = setTimeout(() => {
3224
+ server.close();
3225
+ onError(new Error("Timed out waiting for the browser sign-in (3 min)."));
3226
+ }, CALLBACK_TIMEOUT_MS);
3227
+ timer.unref?.();
3228
+ server.once("close", () => clearTimeout(timer));
3229
+ server.on("error", (err) => {
3230
+ const wrapped = err.code === "EADDRINUSE" ? new Error("Could not bind a local port for the OAuth callback.") : err;
3231
+ rejectPort(wrapped);
3232
+ onError(wrapped);
3233
+ });
3234
+ const port = Number(process.env.SCRAPER_API_LOGIN_PORT) || 8976;
3235
+ server.listen(port, "127.0.0.1", () => {
3236
+ resolvePort({ port: server.address().port, waitForCode });
3237
+ });
3238
+ });
3239
+ }
3240
+ async function exchangeCode({ authDomain, clientId, code, redirectUri, verifier }) {
3241
+ const res = await fetch(`https://${authDomain}/oauth/token`, {
3242
+ method: "POST",
3243
+ headers: { "content-type": "application/x-www-form-urlencoded" },
3244
+ body: new URLSearchParams({
3245
+ grant_type: "authorization_code",
3246
+ client_id: clientId,
3247
+ code,
3248
+ redirect_uri: redirectUri,
3249
+ code_verifier: verifier
3250
+ })
3251
+ });
3252
+ const body = await res.json().catch(() => ({}));
3253
+ if (!res.ok) throw new Error(`Token exchange failed (${res.status}): ${body.error_description || body.error || "unknown"}`);
3254
+ return body.access_token;
3255
+ }
3256
+ async function loginCommand() {
3257
+ const { authDomain, clientId, audience } = resolveAuthSettings();
3258
+ if (!authDomain || !clientId) {
3259
+ throw new Error(
3260
+ "OAuth is not configured. Set the issuer first:\n scraper-api config set auth-domain <your-tenant>.auth0.com\n scraper-api config set client-id <client-id>\n(or export SCRAPER_API_AUTH_DOMAIN / SCRAPER_API_CLIENT_ID)\nAlternatively, skip OAuth: scraper-api auth login --api-key <key>"
3261
+ );
3262
+ }
3263
+ const { verifier, challenge } = pkcePair();
3264
+ const state = base64url(crypto.randomBytes(16));
3265
+ const { port, waitForCode } = await startCallbackServer(state);
3266
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
3267
+ const authorizeUrl = `https://${authDomain}/authorize?` + new URLSearchParams({
3268
+ response_type: "code",
3269
+ client_id: clientId,
3270
+ redirect_uri: redirectUri,
3271
+ scope: "openid profile email offline_access",
3272
+ audience,
3273
+ code_challenge: challenge,
3274
+ code_challenge_method: "S256",
3275
+ state
3276
+ }).toString();
3277
+ process.stderr.write(chalk2.gray("Opening your browser to sign in\u2026\n"));
3278
+ process.stderr.write(chalk2.gray(`If it does not open, visit:
3279
+ ${authorizeUrl}
3280
+ `));
3281
+ try {
3282
+ await open(authorizeUrl);
3283
+ } catch {
3284
+ }
3285
+ const spinner2 = ora2({ stream: process.stderr, text: "Waiting for browser sign-in\u2026" }).start();
3286
+ let code;
3287
+ try {
3288
+ code = await waitForCode;
3289
+ } catch (err) {
3290
+ spinner2.fail("Sign-in failed.");
3291
+ throw err;
3292
+ }
3293
+ spinner2.text = "Exchanging authorization code\u2026";
3294
+ const accessToken = await exchangeCode({ authDomain, clientId, code, redirectUri, verifier });
3295
+ spinner2.text = "Minting your API key\u2026";
3296
+ const label = `CLI ${os2.hostname()}`;
3297
+ const minted = await bearerPost("/v1/account/keys", accessToken, { label });
3298
+ if (!minted?.key) throw new Error("Key minting returned no key.");
3299
+ configSet(KEYS.apiKey, minted.key);
3300
+ spinner2.text = "Fetching account\u2026";
3301
+ let me = null;
3302
+ try {
3303
+ me = await apiGet("/v1/me", {}, minted.key);
3304
+ if (me?.email) configSet(KEYS.email, me.email);
3305
+ } catch {
3306
+ }
3307
+ spinner2.succeed("Signed in.");
3308
+ process.stderr.write(
3309
+ chalk2.green(`\u2713 Logged in${me?.email ? ` as ${chalk2.bold(me.email)}` : ""}. API key saved.
3310
+ `) + (me ? chalk2.gray(` Credits: ${me.credits}${me.plan ? ` (plan: ${me.plan})` : ""}
3311
+ `) : "") + chalk2.gray(` Base: ${apiBase()}
3312
+ `)
3313
+ );
3314
+ }
3315
+
3316
+ // src/commands/session.js
3317
+ import chalk3 from "chalk";
3318
+ async function whoamiCommand(opts = {}, command) {
3319
+ const global = command?.optsWithGlobals?.() ?? {};
3320
+ const apiKey = requireApiKey(global.apiKey);
3321
+ const me = await apiGet("/v1/me", {}, apiKey);
3322
+ if (global.json) {
3323
+ process.stdout.write(JSON.stringify(me, null, global.pretty ? 2 : 0) + "\n");
3324
+ return;
3325
+ }
3326
+ process.stdout.write(
3327
+ `${chalk3.bold("Email:")} ${me?.email ?? chalk3.gray("unknown")}
3328
+ ${chalk3.bold("Plan:")} ${me?.plan ?? chalk3.gray("unknown")}
3329
+ ${chalk3.bold("Credits:")} ${me?.credits}
3330
+ `
3331
+ );
3332
+ }
3333
+ function logoutCommand() {
3334
+ configDelete(KEYS.apiKey);
3335
+ configDelete(KEYS.email);
3336
+ configDelete("access-token");
3337
+ process.stderr.write(chalk3.green("\u2713 Logged out \u2014 stored key and identity removed.\n"));
3338
+ }
3339
+
3340
+ // src/commands/balance.js
3341
+ import chalk4 from "chalk";
3342
+ async function balanceCommand(opts = {}, command) {
3343
+ const global = command?.optsWithGlobals?.() ?? {};
3344
+ const apiKey = requireApiKey(global.apiKey);
3345
+ const me = await apiGet("/v1/me", {}, apiKey);
3346
+ if (global.json) {
3347
+ process.stdout.write(JSON.stringify(me, null, global.pretty ? 2 : 0) + "\n");
3348
+ return;
3349
+ }
3350
+ const credits = me?.credits;
3351
+ process.stdout.write(
3352
+ `${chalk4.bold("Credits:")} ${chalk4.green(credits === "unlimited" ? "unlimited" : credits)}` + (me?.plan ? chalk4.gray(` (plan: ${me.plan})`) : "") + "\n"
3353
+ );
3354
+ }
3355
+
3356
+ // src/commands/list.js
3357
+ import chalk5 from "chalk";
3358
+ function listCommand(platform, opts = {}, command) {
3359
+ const global = command?.optsWithGlobals?.() ?? {};
3360
+ if (global.json || opts.json) {
3361
+ const data = platform ? endpointsForPlatform(platform).map(pick) : endpoints.map(pick);
3362
+ process.stdout.write(JSON.stringify(data, null, global.pretty ? 2 : 0) + "\n");
3363
+ return;
3364
+ }
3365
+ if (platform) {
3366
+ const list = endpointsForPlatform(platform);
3367
+ if (!list.length) {
3368
+ process.stderr.write(chalk5.red(`Unknown platform "${platform}". Known: ${platforms.join(", ")}
3369
+ `));
3370
+ process.exitCode = 1;
3371
+ return;
3372
+ }
3373
+ process.stdout.write(chalk5.bold(`${platform} \u2014 ${list.length} endpoints
3374
+ `));
3375
+ for (const e of list) {
3376
+ const tags = [e.cost > 1 ? chalk5.yellow(`${e.cost} credits`) : null, e.live ? null : chalk5.gray("stub")].filter(Boolean).join(" ");
3377
+ process.stdout.write(` ${chalk5.cyan(`${platform} ${e.action}`)} ${e.summary}${tags ? ` ${tags}` : ""}
3378
+ `);
3379
+ }
3380
+ return;
3381
+ }
3382
+ process.stdout.write(chalk5.bold(`${endpoints.length} endpoints across ${platforms.length} platforms
3383
+ `));
3384
+ for (const p2 of platforms) {
3385
+ const count = endpointsForPlatform(p2).length;
3386
+ process.stdout.write(` ${chalk5.cyan(p2.padEnd(18))} ${count} endpoints
3387
+ `);
3388
+ }
3389
+ process.stdout.write(chalk5.gray("\nRun `scraper-api list <platform>` to see a platform's commands.\n"));
3390
+ }
3391
+ function pick(e) {
3392
+ return {
3393
+ command: `${e.platform} ${e.action}`,
3394
+ platform: e.platform,
3395
+ action: e.action,
3396
+ path: e.path,
3397
+ tool: e.toolName,
3398
+ cost: e.cost,
3399
+ live: e.live,
3400
+ summary: e.summary,
3401
+ params: e.params.map((p2) => ({ name: p2.name, required: p2.required, anyOf: p2.anyOf }))
3402
+ };
3403
+ }
3404
+
3405
+ // src/commands/auth-cmd.js
3406
+ import chalk6 from "chalk";
3407
+ function authLoginCommand(opts = {}) {
3408
+ if (!opts.apiKey) {
3409
+ throw new Error("Provide the key: scraper-api auth login --api-key <key>");
3410
+ }
3411
+ configSet(KEYS.apiKey, opts.apiKey);
3412
+ process.stderr.write(chalk6.green("\u2713 API key stored.\n"));
3413
+ }
3414
+ function authStatusCommand(opts = {}, command) {
3415
+ const global = command?.optsWithGlobals?.() ?? {};
3416
+ let source = null;
3417
+ if (global.apiKey) source = "--api-key flag";
3418
+ else if (configGet(KEYS.apiKey)) source = "config file";
3419
+ else if (process.env.SCRAPER_API_KEY) source = "SCRAPER_API_KEY env";
3420
+ const key = resolveApiKey(global.apiKey);
3421
+ if (!key) {
3422
+ process.stdout.write(chalk6.yellow("Not authenticated. Run `scraper-api login`.\n"));
3423
+ return;
3424
+ }
3425
+ const masked = key.length > 8 ? `${key.slice(0, 4)}\u2026${key.slice(-4)}` : "set";
3426
+ process.stdout.write(`${chalk6.green("Authenticated")} \u2014 key ${masked} (source: ${source})
3427
+ `);
3428
+ }
3429
+
3430
+ // src/commands/config-cmd.js
3431
+ import chalk7 from "chalk";
3432
+ var SECRET_KEYS = /* @__PURE__ */ new Set(["api-key"]);
3433
+ function mask(key, value) {
3434
+ if (value === void 0) return chalk7.gray("(unset)");
3435
+ if (SECRET_KEYS.has(key) && typeof value === "string" && value.length > 8) {
3436
+ return `${value.slice(0, 4)}\u2026${value.slice(-4)}`;
3437
+ }
3438
+ return String(value);
3439
+ }
3440
+ function configSetCommand(key, value) {
3441
+ configSet(key, value);
3442
+ process.stderr.write(chalk7.green(`\u2713 set ${key}
3443
+ `));
3444
+ }
3445
+ function configGetCommand(key) {
3446
+ const value = getConfig().get(key);
3447
+ process.stdout.write(`${mask(key, value)}
3448
+ `);
3449
+ }
3450
+ function configDeleteCommand(key) {
3451
+ configDelete(key);
3452
+ process.stderr.write(chalk7.green(`\u2713 deleted ${key}
3453
+ `));
3454
+ }
3455
+ function configListCommand(opts = {}, command) {
3456
+ const global = command?.optsWithGlobals?.() ?? {};
3457
+ const store = getConfig().store;
3458
+ if (global.json) {
3459
+ const safe = { ...store };
3460
+ if (safe["api-key"]) safe["api-key"] = mask("api-key", safe["api-key"]);
3461
+ process.stdout.write(JSON.stringify(safe, null, global.pretty ? 2 : 0) + "\n");
3462
+ return;
3463
+ }
3464
+ process.stderr.write(chalk7.gray(`# ${configPath()}
3465
+ `));
3466
+ const keys = Object.keys(store);
3467
+ if (!keys.length) {
3468
+ process.stdout.write(chalk7.gray("(empty)\n"));
3469
+ return;
3470
+ }
3471
+ for (const key of keys) {
3472
+ process.stdout.write(`${chalk7.cyan(key)} = ${mask(key, store[key])}
3473
+ `);
3474
+ }
3475
+ }
3476
+
3477
+ // src/commands/agent-cmd.js
3478
+ import fs3 from "node:fs";
3479
+ import path2 from "node:path";
3480
+ import os3 from "node:os";
3481
+ import chalk8 from "chalk";
3482
+ var SERVER_NAME = "scraper-api";
3483
+ function jsonConfigPath(agent) {
3484
+ const home = os3.homedir();
3485
+ if (agent === "cursor") return path2.join(home, ".cursor", "mcp.json");
3486
+ if (agent === "claude") {
3487
+ if (process.platform === "darwin") {
3488
+ return path2.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
3489
+ }
3490
+ if (process.platform === "win32") {
3491
+ return path2.join(process.env.APPDATA || path2.join(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
3492
+ }
3493
+ return path2.join(home, ".config", "Claude", "claude_desktop_config.json");
3494
+ }
3495
+ return null;
3496
+ }
3497
+ function assertNotSymlink(filePath) {
3498
+ try {
3499
+ const stat = fs3.lstatSync(filePath);
3500
+ if (stat.isSymbolicLink()) {
3501
+ throw new Error(`Refusing to write through a symlink: ${filePath}`);
3502
+ }
3503
+ } catch (err) {
3504
+ if (err.code !== "ENOENT") throw err;
3505
+ }
3506
+ }
3507
+ function writeSecure(filePath, contents) {
3508
+ fs3.mkdirSync(path2.dirname(filePath), { recursive: true });
3509
+ assertNotSymlink(filePath);
3510
+ fs3.writeFileSync(filePath, contents, { mode: 384 });
3511
+ try {
3512
+ fs3.chmodSync(filePath, 384);
3513
+ } catch {
3514
+ }
3515
+ }
3516
+ function serverEnv(apiKey) {
3517
+ const env = { SCRAPER_API_KEY: apiKey || "${SCRAPER_API_KEY}" };
3518
+ if (process.env.SCRAPER_API_BASE) env.SCRAPER_API_BASE = process.env.SCRAPER_API_BASE;
3519
+ return env;
3520
+ }
3521
+ function addToJsonAgent(filePath, apiKey) {
3522
+ assertNotSymlink(filePath);
3523
+ let config = {};
3524
+ if (fs3.existsSync(filePath)) {
3525
+ try {
3526
+ config = JSON.parse(fs3.readFileSync(filePath, "utf8"));
3527
+ } catch {
3528
+ throw new Error(`Existing config is not valid JSON: ${filePath}`);
3529
+ }
3530
+ }
3531
+ config.mcpServers = config.mcpServers || {};
3532
+ config.mcpServers[SERVER_NAME] = {
3533
+ command: "npx",
3534
+ args: ["-y", "@scraper-api/mcp"],
3535
+ env: serverEnv(apiKey)
3536
+ };
3537
+ writeSecure(filePath, JSON.stringify(config, null, 2) + "\n");
3538
+ return filePath;
3539
+ }
3540
+ function addToCodex(apiKey) {
3541
+ const filePath = path2.join(os3.homedir(), ".codex", "config.toml");
3542
+ assertNotSymlink(filePath);
3543
+ const existing = fs3.existsSync(filePath) ? fs3.readFileSync(filePath, "utf8") : "";
3544
+ const envKey = apiKey || "${SCRAPER_API_KEY}";
3545
+ const block = `[mcp_servers.${SERVER_NAME}]
3546
+ command = "npx"
3547
+ args = ["-y", "@scraper-api/mcp"]
3548
+ env = { SCRAPER_API_KEY = "${envKey}"${process.env.SCRAPER_API_BASE ? `, SCRAPER_API_BASE = "${process.env.SCRAPER_API_BASE}"` : ""} }
3549
+ `;
3550
+ const sectionRe = new RegExp(`(^|\\n)\\[mcp_servers\\.${SERVER_NAME}\\][\\s\\S]*?(?=\\n\\[|$)`);
3551
+ let next;
3552
+ if (sectionRe.test(existing)) {
3553
+ next = existing.replace(sectionRe, (m, lead) => `${lead}${block.trimEnd()}`);
3554
+ } else {
3555
+ next = existing.trimEnd() ? `${existing.trimEnd()}
3556
+
3557
+ ${block}` : block;
3558
+ }
3559
+ writeSecure(filePath, next.endsWith("\n") ? next : next + "\n");
3560
+ return filePath;
3561
+ }
3562
+ function agentAddCommand(agent, opts = {}, command) {
3563
+ const global = command?.optsWithGlobals?.() ?? {};
3564
+ const target = String(agent || "").toLowerCase();
3565
+ if (!["cursor", "claude", "codex"].includes(target)) {
3566
+ throw new Error(`Unknown agent "${agent}". Supported: cursor, claude, codex.`);
3567
+ }
3568
+ const apiKey = resolveApiKey(global.apiKey);
3569
+ if (!apiKey) {
3570
+ process.stderr.write(
3571
+ chalk8.yellow("\u26A0 No API key found \u2014 writing a ${SCRAPER_API_KEY} placeholder. Run `scraper-api login` first for a baked-in key.\n")
3572
+ );
3573
+ }
3574
+ const written = target === "codex" ? addToCodex(apiKey) : addToJsonAgent(jsonConfigPath(target), apiKey);
3575
+ process.stderr.write(chalk8.green(`\u2713 Added the scraper-api MCP server to ${target}.
3576
+ `));
3577
+ process.stderr.write(chalk8.gray(` ${written}
3578
+ `));
3579
+ process.stderr.write(chalk8.gray(" Restart the agent to load the new tools.\n"));
3580
+ }
3581
+
3582
+ // src/commands/interactive.js
3583
+ import * as p from "@clack/prompts";
3584
+ async function interactiveCommand(opts = {}, command) {
3585
+ const global = command?.optsWithGlobals?.() ?? {};
3586
+ const apiKey = requireApiKey(global.apiKey);
3587
+ p.intro("scraper-api");
3588
+ const platform = await p.select({
3589
+ message: "Platform",
3590
+ options: platforms.map((value) => ({ value, label: value }))
3591
+ });
3592
+ if (p.isCancel(platform)) return void p.cancel("Cancelled.");
3593
+ const list = endpointsForPlatform(platform);
3594
+ const action = await p.select({
3595
+ message: "Endpoint",
3596
+ options: list.map((e) => ({ value: e.action, label: `${e.action} \u2014 ${e.summary}` }))
3597
+ });
3598
+ if (p.isCancel(action)) return void p.cancel("Cancelled.");
3599
+ const endpoint = list.find((e) => e.action === action);
3600
+ const params = {};
3601
+ for (const param of endpoint.params) {
3602
+ const anyOfHint = param.anyOf ? " (any-of group)" : "";
3603
+ const value = await p.text({
3604
+ message: `${param.name}${param.required ? " *" : ""}${anyOfHint}`,
3605
+ placeholder: param.enum ? param.enum.join(" | ") : param.description || ""
3606
+ });
3607
+ if (p.isCancel(value)) return void p.cancel("Cancelled.");
3608
+ if (value) params[param.name] = value;
3609
+ }
3610
+ const spin = p.spinner();
3611
+ spin.start(`GET ${endpoint.path}`);
3612
+ try {
3613
+ const data = await apiGet(endpoint.path, params, apiKey);
3614
+ spin.stop("Done.");
3615
+ p.outro("Response:");
3616
+ emit(data, { format: global.json ? "json" : global.format, pretty: true, output: global.output });
3617
+ } catch (err) {
3618
+ spin.stop("Failed.");
3619
+ p.cancel(err.message);
3620
+ }
3621
+ }
3622
+
3623
+ // src/cli.js
3624
+ function buildProgram() {
3625
+ const program = new Command2();
3626
+ program.name("scraper-api").description("Command-line client for the scraper-api.com scraping API").version("0.1.0").option("--api-key <key>", "API key (overrides config and env)").option("-f, --format <format>", "output format: json | table | csv | markdown", "json").option("--json", "shortcut for --format json").option("--pretty", "pretty-print JSON output").option("-o, --output <file>", "write output to a file instead of stdout").option("--no-color", "disable colored output").option("--verbose", "print request diagnostics to stderr").showHelpAfterError();
3627
+ program.hook("preAction", (thisCommand) => {
3628
+ if (thisCommand.opts().color === false) chalk9.level = 0;
3629
+ });
3630
+ program.command("login").description("Sign in via your browser (OAuth PKCE) and mint an API key").action(loginCommand);
3631
+ program.command("logout").description("Remove the stored API key and identity").action(logoutCommand);
3632
+ program.command("whoami").description("Show the account the active API key belongs to").action((opts, cmd) => whoamiCommand(opts, cmd));
3633
+ const auth = program.command("auth").description("Manual API-key authentication");
3634
+ auth.command("login").description("Store an API key you minted in the dashboard").requiredOption("--api-key <key>", "the API key to store").action(authLoginCommand);
3635
+ auth.command("status").description("Show the active key and its source").action((opts, cmd) => authStatusCommand(opts, cmd));
3636
+ program.command("balance").description("Show remaining credits for the active API key").action((opts, cmd) => balanceCommand(opts, cmd));
3637
+ program.command("list").argument("[platform]", "restrict to one platform").description("List available platforms and endpoint commands").action((platform, opts, cmd) => listCommand(platform, opts, cmd));
3638
+ const config = program.command("config").description("Read and edit persisted CLI settings");
3639
+ config.command("set <key> <value>").description("Set a config value").action(configSetCommand);
3640
+ config.command("get <key>").description("Read a config value").action(configGetCommand);
3641
+ config.command("delete <key>").description("Delete a config value").action(configDeleteCommand);
3642
+ config.command("list").description("List all config values").action((opts, cmd) => configListCommand(opts, cmd));
3643
+ const agent = program.command("agent").description("Wire the scraper-api MCP server into an AI agent");
3644
+ agent.command("add <agent>").description("Add the MCP server to cursor | claude | codex").action((target, opts, cmd) => agentAddCommand(target, opts, cmd));
3645
+ program.command("interactive").description("Guided endpoint picker").action((opts, cmd) => interactiveCommand(opts, cmd));
3646
+ registerEndpointCommands(program);
3647
+ return program;
3648
+ }
3649
+ async function run(argv) {
3650
+ const program = buildProgram();
3651
+ await program.parseAsync(argv);
3652
+ }
3653
+
3654
+ // bin/scraper-api.js
3655
+ run(process.argv).catch((err) => {
3656
+ console.error(err?.message ?? err);
3657
+ process.exit(1);
3658
+ });