All posts
Technical SEO

How do you build Person schema for a licensed real estate investor?

A working JSON-LD Person @graph for a DRE-licensed California real estate operator: hasCredential nested as an EducationalOccupationalCredential, sameAs anchored to DRE.ca.gov, LinkedIn, Wikidata, and operator author pages, and worksFor wired by @id to the Organization. Includes the full block currently shipped on reispark.com.

YK Kuliev

Most investor websites sign every page "By Admin." That is the SEO equivalent of leaving your business card blank. Google's quality systems are built to score the author of a page, and an anonymous string gives them nothing to score.

This post is the working JSON-LD for a credentialed Person — the implementation deep-dive that entity-based SEO for real estate investors defers to. Every snippet below is the exact block currently shipped on reispark.com, written by a licensed California real estate agent (DRE #02006033) who runs two cash-buying businesses. By the end you will have a Person @graph your CMS can copy, sameAs URLs that resolve, and a plan for the Rich Results Test.

What does Person schema do for a licensed real estate investor?

Person schema is JSON-LD that tells Google a real human author runs your site, naming jobTitle, worksFor, sameAs, and hasCredential. For a licensed investor, the Person entity turns an anonymous byline into verifiable identity tied to active DRE license records.

Person schema is the structured-data passport for the human behind your site. It tells Google your author has a name, a job, an employer, a credential, and a public presence beyond this one domain — in a format the indexer does not have to guess at.

For licensed real estate operators that is the difference between a string Google cannot grade and an entity Google can verify. An agent with an active state license and a real track record is not the same kind of author as an anonymous content site. Person schema makes that gap legible to a machine. Post 1 covers what entity-based SEO is at glossary depth; this post implements one of its six entities end to end.

Technical note. Person is a schema.org type and JSON-LD is the recommended serialization. The block lives inside a single <script type="application/ld+json"> element, typically in the page's <head> via your framework's layout. One Person per canonical author, one stable @id URI for that Person across the whole site.

How do you build hasCredential for a California DRE license?

hasCredential is a schema.org property that attaches an EducationalOccupationalCredential to a Person entity. For a California DRE license, set credentialCategory to 'license,' identifier to your license number, and recognizedBy to a sub-Organization that represents the California Department of Real Estate.

hasCredential attaches a license or certification to a Person. The value is not a string — it is a nested `EducationalOccupationalCredential` object whose four weight-bearing properties are name, credentialCategory, identifier, and recognizedBy. name is the credential's official title. credentialCategory is license, certification, or degree. identifier carries the license or certificate number. recognizedBy names the issuing Organization.

The recognizedBy field is where most schema goes soft. A string ("California DRE") gives Google an unverifiable phrase. A nested Organization gives Google a node it can link to its own Knowledge Graph. Google's NLP layer reads recognizedBy looking for a known issuer, not free text.

For an active California real estate license, a complete entry looks like this:

json
{
  "@type": "EducationalOccupationalCredential",
  "name": "California Real Estate Salesperson License",
  "credentialCategory": "license",
  "identifier": "02006033",
  "recognizedBy": {
    "@type": "GovernmentOrganization",
    "name": "California Department of Real Estate",
    "url": "https://www.dre.ca.gov"
  },
  "url": "https://www2.dre.ca.gov/publicasp/pplinfo.asp?License_id=02006033"
}

The url at the bottom is the public state record for that specific license. Click it and the database confirms the license is active. That single URL is the heart of the E-E-A-T signal — a third party Google trusts vouches for the credential. Two practical rules: the identifier should be the bare license number, not a decorated string, and recognizedBy.url should be the issuing body's homepage rather than a search page.

How does sameAs reconcile a person across DRE.ca.gov, LinkedIn, and operator author pages?

sameAs lists external URLs that prove the same Person exists across the open web. For a licensed investor, point sameAs to your DRE.ca.gov license verification page, your LinkedIn profile, your Wikidata entity page, and any author page on operator sites.

sameAs handles cross-domain entity reconciliation. Each URL in the array is another public page that names the same Person. Google walks the array, fetches each URL, and merges what it finds back into one entity.

A "good" sameAs URL is publicly resolvable, returns 200, and names the same person. A "bad" sameAs URL is a private profile, a 404, or a brand profile linked from a Person schema. The fastest way to weaken an otherwise-solid Person @graph is to ship sameAs URLs that do not return your name.

For a licensed California operator, the canonical chain looks like this:

json
"sameAs": [
  "https://www2.dre.ca.gov/publicasp/pplinfo.asp?License_id=02006033",
  "https://www.linkedin.com/in/yhlas-yk-kuliev-cpa-cisa-8520ab5",
  "https://www.wikidata.org/wiki/Q138736275",
  "https://yubahomebuyer.com",
  "https://fasthomebuyercalifornia.com/yk-kuliev-california-cash-home-buyer"
]

The DRE verification URL is the highest-value entry: state government domain, per-licensee deep link, and a page that itself states the license is active. Knowledge Graph systems treat government credential URLs as strong identity anchors — much stronger than a social profile.

LinkedIn is second-strongest because Google already crawls LinkedIn aggressively. Your profile should name the same credential your schema does. A contradiction between the two downweights both ends.

Wikidata is the third anchor for operators with a Wikidata entity. The Q-number page is a structured Person record other domains can point at, which strengthens reconciliation even when Google has partial confidence in any single source.

The operator author pages — Yuba Home Buyer and Fast Home Buyer California — reinforce the cross-brand claim. Each ships its own Person schema that names the same human and points back through its own sameAs. When the sites converge on the same credential, LinkedIn handle, and Q-number, Google merges the entities into one identifiable author.

How do you wire Person to Organization with @id?

@id is a stable URI that names an entity uniquely so other schema blocks can reference it. Set Person's @id to a founder fragment, set Organization's @id to a root URI, then reference each entity from the other using @id.

@id is the JSON-LD property that gives an entity a stable, unique URI. It is not a URL Google has to fetch — it is a name. Other schema blocks reference an entity by its @id instead of repeating the entity's properties. Done well, your entire site emits one Person and one Organization even though dozens of pages reference both.

The canonical pattern uses fragment URIs anchored on your root domain. Person sits at https://reispark.com/#yk and Organization sits at https://reispark.com/#organization. The Person's worksFor property points to the Organization's @id. The Organization's founder property points to the Person's @id. That bidirectional reference is what stops Google from treating each page as a separate Person.

When the homepage emits a lighter Person at #yk and the about page emits a richer Person at the same #yk, Google merges them by @id. When a blog post's BlogPosting schema uses "author": { "@id": "https://reispark.com/#yk" }, the post inherits everything the about page declares. The rule that makes this work is mechanical: the same @id must appear everywhere on the site that names the entity. One typo in one place and Google has to guess whether you mean the same Person, which is the failure mode @id exists to prevent.

Technical note. JSON-LD allows forward references — the resolver does not require the referenced node to appear earlier in the document. So a Person worksFor can reference an Organization defined two scripts down, in a sibling @graph, or even on a different page. The @id is the link, not the order.

When an Organization has more than one founder

Some operator businesses have two or more founders. Fast Home Buyer California is one — YK as the DRE-licensed agent, Alsu as a California-licensed CPA. The pattern: the @graph carries one Person node per founder with distinct @id values (#yk and #alsu), and the Organization's founder property becomes an array of @id references. Each founder reconciles independently against its own verification chain — DRE for YK, California Board of Accountancy for Alsu. One Organization, two credentialed founders, two issuing bodies. The full multi-founder JSON-LD walkthrough is the subject of Post 3.

What does the full JSON-LD look like for a licensed investor?

A complete Person block for a licensed investor includes @id, name, jobTitle, worksFor referencing the Organization @id, sameAs with public verification URLs, and a hasCredential entry for the license held. The credential names credentialCategory, identifier, and recognizedBy with a sub-Organization.

Here is the complete Person block currently shipped at /about/yk-kuliev. It lives in a single <script type="application/ld+json"> element in the page's <head> and shares the same @id the site-wide layout uses for its lighter Person reference. Google merges the two by @id, and the rich definition takes precedence on the canonical author URL.

Read it once before reading the annotations:

json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Person",
      "@id": "https://reispark.com/#yk",
      "name": "YK Kuliev",
      "givenName": "Yhlas",
      "familyName": "Kuliev",
      "alternateName": "Yhlas Kuliev",
      "jobTitle": "Founder & Lead SEO Engineer",
      "url": "https://reispark.com/about/yk-kuliev",
      "worksFor": { "@id": "https://reispark.com/#organization" },
      "hasCredential": {
        "@type": "EducationalOccupationalCredential",
        "name": "California Real Estate Salesperson License",
        "credentialCategory": "license",
        "identifier": "02006033",
        "recognizedBy": {
          "@type": "GovernmentOrganization",
          "name": "California Department of Real Estate",
          "url": "https://www.dre.ca.gov"
        },
        "url": "https://www2.dre.ca.gov/publicasp/pplinfo.asp?License_id=02006033"
      },
      "sameAs": [
        "https://www2.dre.ca.gov/publicasp/pplinfo.asp?License_id=02006033",
        "https://www.linkedin.com/in/yhlas-yk-kuliev-cpa-cisa-8520ab5",
        "https://www.wikidata.org/wiki/Q138736275",
        "https://yubahomebuyer.com",
        "https://fasthomebuyercalifornia.com/yk-kuliev-california-cash-home-buyer"
      ]
    }
  ]
}

Three things worth marking. The hasCredential value is a single object, not an array — there is one credential here, so wrapping it in [ ... ] adds noise without adding signal. The DRE entry carries a full identifier, a GovernmentOrganization recognizedBy with its own url, and a public verification URL on the credential itself; that triplet is what makes Google's NLP entity layer treat the credential as verifiable rather than asserted. The worksFor reference points at #organization by @id — the Organization @graph lives in the site-wide layout, not duplicated here.

For a single-founder brand this is the whole pattern. For multiple founders, follow the previous section.

How do you validate Person schema in Google's Rich Results Test?

Google's Rich Results Test parses JSON-LD on a URL and reports validation errors, warnings, and detected entity types. For Person schema, paste the live URL, confirm Person is detected, expand the result, and verify hasCredential, sameAs, and worksFor reconcile correctly.

Google publishes two validators that check Person schema differently. The Schema Markup Validator at validator.schema.org checks JSON-LD syntax against the schema.org vocabulary. Google's Rich Results Test validates rich-result eligibility and tells you what Google's parser actually detected on the page. Use both; the Rich Results Test is the one that matters for SEO.

Paste your live URL, click "Test URL," and wait for the fetch and parse. The result page lists every schema block Google detected, grouped by type — BlogPosting, BreadcrumbList, Organization, Person, WebSite. Each expands to show parsed properties.

For a Person block, walk five checks in order. First, confirm Person is detected — if it is not, your <script type="application/ld+json"> tag is missing or malformed. Second, expand the result and verify the hasCredential entry shows the right credentialCategory and a nested recognizedBy Organization, not a string. Third, check that sameAs has every URL you expected; a missing entry usually means a 404 or a redirect Google cannot resolve. Fourth, confirm worksFor resolves to the Organization @id. Fifth, click the DRE verification URL from inside the result and confirm it loads — that is the third-party check the rest of the schema implies.

A note on what is not displayed. The Rich Results Test only shows cards for schema types with rich-result UI templates. Person and WebSite render through the index but do not get displayed cards in the test, even when they parse cleanly. So "three valid items detected" on a page that emits five types is not an error. Click "View parsed structured data" and read the JSON-LD directly to confirm the rest.

Why does a credentialed Person entity outweigh an anonymous byline for E-E-A-T?

E-E-A-T weights Experience, Expertise, Authoritativeness, and Trust. A credentialed Person entity beats an anonymous byline because Google can match jobTitle, hasCredential, and sameAs against external authoritative sources, then score the same author across every page they sign on the site.

E-E-A-T stands for Experience, Expertise, Authoritativeness, and Trust. It is the framework Google's Search Quality Evaluator Guidelines use to score "who wrote this and why should we trust them" — the same question every reader silently asks when a page loads. The framework is human, but the signals Google's systems use to operationalize it are mechanical.

A page signed "By Admin" or "By The Team" gives Google's NLP entity layer one string and no credential. There is nothing to verify, nothing to cross-reference, and nothing that compounds when the same author signs the next post on the same site. The author is, for entity-grading purposes, anonymous.

A page that signs with a Person @graph containing jobTitle, hasCredential with recognizedBy, and sameAs URLs to public verification records gives Google's NLP layer a different problem. The system can match jobTitle against industry vocabularies, match hasCredential.identifier against the issuing body's records via recognizedBy.url, and match sameAs URLs against external pages that name the same Person. Every successful match is a Trust signal, and the signals compound.

Compounding is what makes credentialed authorship matter on the medium time horizon. The fifth time the same @id signs a post, Google has five chances to merge the entity with its external verification chain. By the time a twelve-post series ships, every byline resolves to a single verified Person — not a string Google grades in isolation each time. That is the E-E-A-T move Person schema makes.

How does REI Spark ship Person schema by default?

REI Spark builds ship Person schema in the root layout, wired to the operator's actual DRE license. recognizedBy points at the California Department of Real Estate, identifier carries the license number, and sameAs lists public registry URLs. Default, not upsell.

On a REI Spark custom Next.js website for licensed real estate investors, the Person @graph is wired into the root layout from day one. Every page inherits a credentialed Person reference; the canonical author URL ships the rich version with full hasCredential, recognizedBy, and sameAs. The pattern in this post is the production default, not an enterprise tier.

Three things follow. Every blog post's BlogPosting schema uses "author": { "@id": "https://yoursite.com/#yk" } automatically — no per-post configuration, no risk of "By The Team" sneaking in. The DRE verification URL in recognizedBy.url and the matching sameAs entry come from a single constants file, so a license update or recertification propagates everywhere with one edit. The credential carries its issuing body as a typed Organization, not a string, which makes Google's entity reconciliation work by default rather than by accident.

For operators on template platforms, the identity layer is usually the part the defaults leave for you to assemble. None of the pieces are difficult once you have seen the working JSON-LD; they are just rarely shipped together. REI Spark's position is to ship them all by default and keep the constants file small enough to audit.

What to do today

Open Google's Rich Results Test in another tab and paste your site's about-page URL. If it does not return a Person, your first action is to ship a Person @graph matching the snippet in H2 #5, with your real DRE number, your real recognizedBy block, and at least one resolving sameAs. If it returns a Person but hasCredential is empty, your second action is to add it. The work is small and the E-E-A-T compounding is real. For the vocabulary behind these properties, start with entity-based SEO for real estate investors. Or see how REI Spark builds it by default.