@nuxtseo/protocol 0.4.1 → 0.5.1

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.
@@ -361,6 +361,35 @@ export const liveBacklinksQuerySchema = schema.strictObject({
361
361
  limit: schema.coerce.number().int().min(1).max(1000).default(100),
362
362
  });
363
363
  const nullableCount = schema.number().nullable();
364
+ /**
365
+ * A Link Spam Network: referring domains that each place one generated anchor
366
+ * with the Site's host written into it, one link per domain. It is read from
367
+ * the retained Backlinks rows, never from `spamScore`. Google ignores most
368
+ * links like these, so the summary counts them apart from the profile.
369
+ */
370
+ export const linkSpamNetworkSchema = defineResponseObject({
371
+ /** Stable for one anchor template on one Site. */
372
+ id: schema.string().min(1),
373
+ /** The anchor as the provider reported it, with the Site's host in it. */
374
+ anchor: schema.string().min(1),
375
+ referringDomains: schema.number().int().nonnegative(),
376
+ backlinks: schema.number().int().nonnegative(),
377
+ /** When the provider first saw the anchor. */
378
+ firstSeen: schema.string().nullable(),
379
+ /** When the newest sampled member domain was first seen. */
380
+ latestSeen: schema.string().nullable(),
381
+ /** The linking page path every sampled member shares, or null when they differ. */
382
+ linkingPath: schema.string().nullable(),
383
+ /** Paths on the Site that the sampled members link to, most linked first. */
384
+ targetPaths: schema.array(schema.string()),
385
+ /** Other Sites whose anchors carry the same template. */
386
+ otherSites: schema.number().int().nonnegative(),
387
+ });
388
+ /** The profile totals with every Link Spam Network removed. */
389
+ const profileWithoutNetworksSchema = defineResponseObject({
390
+ referringDomains: schema.number().int().nonnegative(),
391
+ backlinks: schema.number().int().nonnegative(),
392
+ });
364
393
  export const backlinksSummaryDataSchema = defineResponseObject({
365
394
  target: schema.string().min(1),
366
395
  rank: nullableCount,
@@ -375,6 +404,21 @@ export const backlinksSummaryDataSchema = defineResponseObject({
375
404
  referringIps: nullableCount,
376
405
  referringSubnets: nullableCount,
377
406
  referringPages: nullableCount,
407
+ /**
408
+ * The Site's Link Spam Networks, largest first. Empty for most Sites. The
409
+ * counts above still include them.
410
+ *
411
+ * A producer must send it and `withoutLinkSpamNetworks`. A client treats
412
+ * both as optional, because a client newer than the deployed server reads
413
+ * answers that predate the fields.
414
+ */
415
+ linkSpamNetworks: schema.array(linkSpamNetworkSchema.producer),
416
+ /**
417
+ * `referringDomains` and `backlinks` without the networks. Null when the
418
+ * Site has no network, or when the provider returned no total to subtract
419
+ * from.
420
+ */
421
+ withoutLinkSpamNetworks: profileWithoutNetworksSchema.producer.nullable(),
378
422
  evidence: liveBacklinksEvidenceProducerSchema,
379
423
  }, {
380
424
  target: schema.string().min(1),
@@ -390,6 +434,8 @@ export const backlinksSummaryDataSchema = defineResponseObject({
390
434
  referringIps: nullableCount,
391
435
  referringSubnets: nullableCount,
392
436
  referringPages: nullableCount,
437
+ linkSpamNetworks: schema.array(linkSpamNetworkSchema.client).optional(),
438
+ withoutLinkSpamNetworks: profileWithoutNetworksSchema.client.nullable().optional(),
393
439
  evidence: liveBacklinksEvidenceClientSchema,
394
440
  });
395
441
  export const backlinksSummaryResponseSchema = defineSuccessEnvelope(backlinksSummaryDataSchema);
@@ -409,7 +455,7 @@ export const backlinksSummaryOperation = defineOperation({
409
455
  lifecycle: { introduced: '1.2.0' },
410
456
  docs: {
411
457
  summary: 'Get Backlinks summary',
412
- description: 'Returns whole-domain inbound link totals for the Site: backlinks, referring domains, broken counts, and the domain rank. Cached reads consume no units.',
458
+ description: 'Returns whole-domain inbound link totals for the Site: backlinks, referring domains, broken counts, and the domain rank. It also lists the link spam networks in the retained rows, and the totals without them. Cached reads consume no units.',
413
459
  tags: ['Research'],
414
460
  examples: [{
415
461
  request: { params: { siteId: 'site_01JXYZ' } },
@@ -430,6 +476,18 @@ export const backlinksSummaryOperation = defineOperation({
430
476
  referringIps: 350,
431
477
  referringSubnets: 322,
432
478
  referringPages: 4800,
479
+ linkSpamNetworks: [{
480
+ id: 'lsn_5d1c07a2',
481
+ anchor: 'Premium SEO Link Building Experts Helping example.com Websites Rank Higher',
482
+ referringDomains: 42,
483
+ backlinks: 42,
484
+ firstSeen: '2026-08-02T00:00:00.000Z',
485
+ latestSeen: '2026-09-20T00:00:00.000Z',
486
+ linkingPath: '/dir/organic-visibility-backlinks-86677',
487
+ targetPaths: ['/'],
488
+ otherSites: 201,
489
+ }],
490
+ withoutLinkSpamNetworks: { referringDomains: 370, backlinks: 5078 },
433
491
  evidence: { _tag: 'live', provider: 'dataforseo', spend: { key: 'dataforseo', units: 1 } },
434
492
  },
435
493
  meta: { requestId: 'req_01JXYZ', version: '1.0' },
@@ -450,6 +508,14 @@ export const referringDomainSchema = defineResponseObject({
450
508
  lostDate: schema.string().nullable(),
451
509
  /** 90-day referral sessions from this domain, or null when no Web Analytics property is linked. */
452
510
  sessions: nullableCount,
511
+ /**
512
+ * The id of the Link Spam Network this domain is a known member of, as in
513
+ * the summary's `linkSpamNetworks`. `null` means not known to be in one,
514
+ * never verified clean: membership is known only for the domains the
515
+ * retained rows sampled or the Timeline recorded. A producer must send it;
516
+ * a client treats it as optional, because older servers do not.
517
+ */
518
+ linkSpamNetworkId: schema.string().min(1).nullable(),
453
519
  }, {
454
520
  domain: schema.string().min(1),
455
521
  rank: nullableCount,
@@ -461,6 +527,7 @@ export const referringDomainSchema = defineResponseObject({
461
527
  firstSeen: schema.string().nullable(),
462
528
  lostDate: schema.string().nullable(),
463
529
  sessions: nullableCount,
530
+ linkSpamNetworkId: schema.string().min(1).nullable().optional(),
464
531
  });
465
532
  export const referringDomainsDataSchema = defineResponseObject({
466
533
  target: schema.string().min(1),
@@ -492,7 +559,7 @@ export const referringDomainsOperation = defineOperation({
492
559
  lifecycle: { introduced: '1.2.0' },
493
560
  docs: {
494
561
  summary: 'List referring domains',
495
- description: 'Returns the domains that link to the Site, highest authority first, with spam score and broken-link counts. When a Web Analytics property is linked, each row also carries its 90-day referral sessions, so a link that sends nobody is visible. Cached reads consume no units.',
562
+ description: 'Returns the domains that link to the Site, highest authority first, with spam score and broken-link counts. When a Web Analytics property is linked, each row also carries its 90-day referral sessions, so a link that sends nobody is visible. A row that is a known member of a link spam network carries its id. Cached reads consume no units.',
496
563
  tags: ['Research'],
497
564
  examples: [{
498
565
  request: { params: { siteId: 'site_01JXYZ' }, query: { limit: 100 } },
@@ -512,6 +579,7 @@ export const referringDomainsOperation = defineOperation({
512
579
  firstSeen: '2026-02-09 20:43:50 +00:00',
513
580
  lostDate: null,
514
581
  sessions: 143,
582
+ linkSpamNetworkId: null,
515
583
  }],
516
584
  total: 1,
517
585
  limit: 100,
@@ -533,6 +601,14 @@ export const backlinkAnchorSchema = defineResponseObject({
533
601
  spamScore: nullableCount,
534
602
  firstSeen: schema.string().nullable(),
535
603
  lostDate: schema.string().nullable(),
604
+ /**
605
+ * The id of the Link Spam Network whose generated sentence this anchor is,
606
+ * as in the summary's `linkSpamNetworks`. Exact, because the anchor is the
607
+ * evidence. `null` means not known to be in one, never verified clean. A
608
+ * producer must send it; a client treats it as optional, because older
609
+ * servers do not.
610
+ */
611
+ linkSpamNetworkId: schema.string().min(1).nullable(),
536
612
  }, {
537
613
  anchor: schema.string(),
538
614
  rank: nullableCount,
@@ -543,6 +619,7 @@ export const backlinkAnchorSchema = defineResponseObject({
543
619
  spamScore: nullableCount,
544
620
  firstSeen: schema.string().nullable(),
545
621
  lostDate: schema.string().nullable(),
622
+ linkSpamNetworkId: schema.string().min(1).nullable().optional(),
546
623
  });
547
624
  export const backlinkAnchorsDataSchema = defineResponseObject({
548
625
  target: schema.string().min(1),
@@ -574,7 +651,7 @@ export const backlinkAnchorsOperation = defineOperation({
574
651
  lifecycle: { introduced: '1.2.0' },
575
652
  docs: {
576
653
  summary: 'List Backlink anchors',
577
- description: 'Returns the anchor text distribution of the Site inbound links, most linked first. Use it to find over-optimised or off-brand anchors. Cached reads consume no units.',
654
+ description: 'Returns the anchor text distribution of the Site inbound links, most linked first. Use it to find over-optimised or off-brand anchors. An anchor that is the generated sentence of a link spam network carries its id. Cached reads consume no units.',
578
655
  tags: ['Research'],
579
656
  examples: [{
580
657
  request: { params: { siteId: 'site_01JXYZ' }, query: { limit: 100 } },
@@ -593,6 +670,7 @@ export const backlinkAnchorsOperation = defineOperation({
593
670
  spamScore: 3,
594
671
  firstSeen: '2026-01-04 11:02:11 +00:00',
595
672
  lostDate: null,
673
+ linkSpamNetworkId: null,
596
674
  }],
597
675
  total: 1,
598
676
  limit: 100,