An SEO proposal template: findings, plan and pricing on one page

An SEO proposal does three jobs: show what you found, say what you will do, and give a price. Put them on one page, in that order, and send the page as a link.

An SEO proposal needs three things on one page: what you found on the prospect's site, what you will do about it, and what it costs. Open with a two-sentence summary, then the findings, the plan and the price.

The question is common enough that Quora has a thread titled "How do I write a quote for a web development freelance project?" SEO work raises the same worry: the prospect has to see the findings and the price together before they will say yes.

Here is a page you can copy. Pick a package and tick the monthly check-in to watch the total change. Every name and number is made up, so replace them with yours.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SEO proposal: Alder Dental</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; font-family: system-ui, sans-serif; color: #1d2330; background: #f4f5f7; line-height: 1.55; }
  .page { max-width: 740px; margin: 0 auto; padding: 22px 18px 32px; background: #fff; }
  .tag { display: inline-block; font-size: 12px; font-weight: 700; letter-spacing: .4px; color: #9a3412; background: #ffedd5; border-radius: 999px; padding: 2px 10px; }
  h1 { font-size: 26px; line-height: 1.2; margin: 10px 0 6px; }
  .meta { color: #5b6473; font-size: 14px; margin: 0 0 14px; }
  h2 { font-size: 18px; margin: 26px 0 8px; }
  p { margin: 0 0 10px; }
  .sum { background: #eff6ff; border-radius: 12px; padding: 12px 14px; }
  .sum b { color: #1d4ed8; }
  .find { display: grid; gap: 8px; }
  .find div { border: 1px solid #e1e4ea; border-radius: 10px; padding: 10px 12px; }
  .find b { display: block; font-size: 15px; }
  .find span { font-size: 14px; color: #374151; }
  .find em { font-style: normal; font-size: 12px; font-weight: 700; color: #9a3412; background: #ffedd5; border-radius: 999px; padding: 1px 8px; margin-right: 6px; }
  ol.plan { padding-left: 20px; margin: 0; }
  ol.plan li { margin-bottom: 8px; }
  .pk { display: grid; gap: 8px; }
  .pk label { display: block; border: 1.5px solid #d5d9e0; border-radius: 12px; padding: 10px 12px; cursor: pointer; }
  .pk label:has(input:checked) { border-color: #1d4ed8; background: #eff6ff; }
  .pk input { margin-right: 8px; }
  .pk .top { display: flex; justify-content: space-between; gap: 10px; font-weight: 700; }
  .pk small { display: block; margin: 3px 0 0 24px; color: #5b6473; font-size: 13px; }
  .add { margin: 12px 0 0; padding: 10px 12px; background: #f9fafb; border-radius: 10px; }
  .add label { display: flex; gap: 8px; align-items: flex-start; font-size: 14px; cursor: pointer; }
  .total { margin-top: 12px; display: flex; justify-content: space-between; align-items: baseline; border-top: 2px solid #1d2330; padding-top: 10px; }
  .total b { font-size: 24px; }
  .not { border-left: 3px solid #d5d9e0; margin: 0; padding-left: 28px; color: #374151; font-size: 14px; }
  .not li { margin-bottom: 4px; }
  .next { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 12px; padding: 12px 14px; }
  @media (max-width: 480px) { h1 { font-size: 22px; } }
</style>
</head>
<body>
<main class="page">
  <span class="tag">SAMPLE - replace every name and number</span>
  <h1>SEO proposal for Alder Dental</h1>
  <p class="meta">Prepared by Sample Studio - 1 October 2026 - valid for 14 days</p>

  <div class="sum">
    <b>In short:</b> your service pages are hard for search engines to tell apart, and the site loads slowly on phones. I will fix both over 8 weeks and report on the Search Console numbers each month. Fixed price from <b>$900</b>.
  </div>

  <h2>What I found</h2>
  <div class="find">
    <div><b><em>HIGH</em>Four service pages share one title</b><span>Whitening, implants, braces and check-ups all show the same title in the browser tab. Each page needs its own.</span></div>
    <div><b><em>HIGH</em>The home page is slow on a phone</b><span>The largest element takes 4.1 seconds to appear in my test. Google's guide for a good experience is 2.5 seconds or less.</span></div>
    <div><b><em>MEDIUM</em>No page for the second clinic</b><span>The second address appears only in the footer, so there is nothing for a search to land on.</span></div>
  </div>

  <h2>The plan</h2>
  <ol class="plan">
    <li><b>Weeks 1-2: titles and descriptions.</b> One unique title and description for each of the 12 pages.</li>
    <li><b>Weeks 3-5: speed.</b> Resize the images and remove the two scripts the pages do not use.</li>
    <li><b>Weeks 6-8: clinic page and links.</b> A page for the second clinic, linked from the menu.</li>
  </ol>
  <p style="margin-top:10px">I check Search Console at week 4 and week 8. Changes can take weeks to show, so I report what moved and what has not yet.</p>

  <h2>Price - choose one</h2>
  <div class="pk" id="pk">
    <label><span class="top"><span><input type="radio" name="pk" value="900" data-name="Audit and plan" checked>Audit and plan</span><span>$900</span></span><small>The findings and plan above. You or your web person make the changes.</small></label>
    <label><span class="top"><span><input type="radio" name="pk" value="2400" data-name="Audit, plan and fixes">Audit, plan and fixes</span><span>$2,400</span></span><small>I make all the changes in the plan, weeks 1-8.</small></label>
  </div>
  <div class="add">
    <label><input type="checkbox" id="mo" value="300"><span>Add monthly check-in after week 8: one page report, $300 a month</span></label>
  </div>
  <div class="total"><span id="what">Audit and plan</span><b id="sum">$900</b></div>
  <p class="meta" id="mon" style="margin-top:6px"></p>

  <h2>What I do not promise</h2>
  <ul class="not">
    <li>A position on Google. No one can guarantee a ranking.</li>
    <li>Writing new articles or product copy. That is a separate quote.</li>
    <li>Changes to the booking system.</li>
  </ul>

  <h2>Next step</h2>
  <div class="next">Reply to my email with the package you want and I will send a start date. Questions first? Reply with them and I will answer within two working days.</div>
</main>

<script>
  const fmt = (n) => '$' + n.toLocaleString('en-US');
  const sum = document.getElementById('sum');
  const what = document.getElementById('what');
  const mon = document.getElementById('mon');
  const mo = document.getElementById('mo');

  function update() {
    const pick = document.querySelector('input[name="pk"]:checked');
    sum.textContent = fmt(Number(pick.value));
    what.textContent = pick.dataset.name;
    mon.textContent = mo.checked ? 'Plus $300 a month from week 9, cancel any month.' : '';
  }
  document.querySelectorAll('input').forEach((i) => i.addEventListener('change', update));
  update();
</script>
</body>
</html>
A sample SEO proposal for a made-up dental clinic: summary, findings, plan, two priced options, what is not included and the next step.

Make it with AI

An AI is quick at laying out a page from rough notes. It will also fill gaps with plausible numbers, such as a speed score you never measured. The prompt has to forbid that.

Anthropic's documentation on reducing hallucinations suggests letting the model say it does not have enough information, and basing answers on text you provide. It adds that these techniques reduce hallucinations but do not eliminate them.

Paste this into ChatGPT, Claude or Gemini and replace the notes at the bottom.

Turn my notes into a one-page SEO proposal as ONE self-contained HTML file.

Rules:
- Use only the facts in my notes. Do not invent numbers, scores, traffic or results.
- If something is missing, write [ADD: what is missing] instead of guessing.
- Sections in order: two-sentence summary, What I found (3-5 items with evidence), The plan (steps with weeks), Price (options with one total each), What I do not promise, Next step.
- Do not promise rankings, positions or traffic.
- The next step is "reply to my email". No form, no Accept button.
- Plain HTML and CSS, readable on a phone, no external images or scripts.

My notes:
Prospect:
Findings and the evidence for each:
Plan and weeks:
Options and prices:
Not included:

Then check the result before you send it:

  • Numbers. Match every figure to your audit. Delete any you cannot find there.
  • [ADD] marks. Fill each one with a fact you can back up, or remove the sentence.
  • Promises. Remove any claim about a position or a traffic gain.
  • Prices. Add up each option by hand. Check what is and is not included.
  • Private data. Anyone with the link can open the page, so leave out logins and the prospect's private figures.

Copy the whole HTML the AI gave you. Saving HTML from ChatGPT shows how. Paste it into a NOS document and the page renders as written, scripts included.

The route from your audit notes to an AI draft, your check, and a share link.
The route from your audit notes to an AI draft, your check, and a share link.

Choose Create share link. HTML to link walks through it. Open the link on your phone once, as the prospect would, then paste it into your email.

The prospect sees the page in a browser, on a phone or a laptop, with no account to create. They cannot edit it. If you correct a price later, you edit the code and the same link shows the new version.

I did not verify how any particular mail or chat app previews a link, so check by sending it to yourself first. For the wider habit, see how to send a proposal as a link.

What goes on the page

Keep the structure fixed so the prospect always knows where to look.

The five blocks of the proposal in order: findings, plan, price, not included, next step.
The five blocks of the proposal in order: findings, plan, price, not included, next step.

Findings should show evidence, not adjectives. Google's SEO starter guide says a good title is unique to the page, clear and concise, so "four pages share one title" is a finding the prospect can check.

Core Web Vitals give another. As of October 2026, Google's documentation says to aim for the largest element to load within 2.5 seconds.

The plan needs weeks and a check date. Google says some changes take effect in a few hours and others can take several months, and suggests waiting a few weeks to judge your work.

Say when you will look and what you will read. The Search Console Performance report shows clicks, impressions, average click-through rate and average position.

Price works best as one to three options with one total each. If you would rather tie price to scope, the sample below lets the prospect tick findings and see the cost.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Findings and scope</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; padding: 14px; font-family: system-ui, sans-serif; color: #1d2330; background: #f4f5f7; line-height: 1.5; }
  .wrap { max-width: 720px; margin: 0 auto; background: #fff; border-radius: 14px; padding: 16px; }
  .tag { display: inline-block; font-size: 12px; font-weight: 700; color: #9a3412; background: #ffedd5; border-radius: 999px; padding: 2px 10px; }
  h1 { font-size: 20px; margin: 8px 0 4px; }
  .sub { margin: 0 0 12px; font-size: 14px; color: #5b6473; }
  .filters { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
  .filters button { font: inherit; font-size: 14px; padding: 5px 12px; border-radius: 999px; border: 1px solid #c9ceda; background: #fff; cursor: pointer; }
  .filters button[aria-pressed="true"] { background: #1d4ed8; border-color: #1d4ed8; color: #fff; }
  .item { border: 1px solid #e1e4ea; border-radius: 10px; margin-bottom: 8px; }
  .item[hidden] { display: none; }
  .head { display: flex; gap: 10px; align-items: center; padding: 10px 12px; }
  .head input { width: 18px; height: 18px; flex: none; }
  .head .t { flex: 1; font-weight: 600; font-size: 15px; }
  .pri { font-size: 12px; font-weight: 700; border-radius: 999px; padding: 1px 8px; white-space: nowrap; }
  .High { background: #fde2da; color: #9a3412; } .Medium { background: #fef3c7; color: #92400e; } .Low { background: #e5e7eb; color: #374151; }
  details { padding: 0 12px 10px 40px; font-size: 14px; color: #374151; }
  summary { cursor: pointer; color: #1d4ed8; font-size: 13px; }
  details p { margin: 6px 0 0; }
  .bar { position: sticky; bottom: 0; margin-top: 12px; background: #1d2330; color: #fff; border-radius: 12px; padding: 10px 14px; display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
  .bar b { font-size: 20px; }
  .rate { font-size: 13px; opacity: .85; }
</style>
</head>
<body>
<div class="wrap">
  <span class="tag">SAMPLE - replace with your findings</span>
  <h1>Findings and scope</h1>
  <p class="sub">Tick what you want me to fix. The price follows your ticks. Sample rate: $60 an hour.</p>

  <div class="filters" id="filters">
    <button aria-pressed="true" data-f="all">All</button>
    <button aria-pressed="false" data-f="High">High</button>
    <button aria-pressed="false" data-f="Medium">Medium</button>
    <button aria-pressed="false" data-f="Low">Low</button>
  </div>

  <div id="list"></div>

  <div class="bar">
    <span><span id="hrs">0</span> hours <span class="rate">x $60</span></span>
    <b id="cost">$0</b>
  </div>
</div>

<script>
  const RATE = 60;
  const items = [
    { t: 'Four pages share one title', p: 'High', h: 4, ev: 'Whitening, implants, braces and check-ups all show the same title in the tab.', fix: 'Write a unique title and description for each page.' },
    { t: 'Home page is slow on a phone', p: 'High', h: 8, ev: 'The largest element appears after 4.1 seconds in my test.', fix: 'Resize the hero image and remove two unused scripts.' },
    { t: 'No page for the second clinic', p: 'Medium', h: 5, ev: 'The address appears only in the footer.', fix: 'Add a clinic page and link it from the menu.' },
    { t: 'Images have no descriptions', p: 'Medium', h: 3, ev: '31 of 40 images have empty alt text.', fix: 'Write short alt text for each image that shows something.' },
    { t: 'Old team page returns an error', p: 'Low', h: 1, ev: '/team is linked from the footer and shows a not-found page.', fix: 'Point the link at the current page.' }
  ];

  const list = document.getElementById('list');
  items.forEach((it, i) => {
    const d = document.createElement('div');
    d.className = 'item';
    d.dataset.p = it.p;
    d.innerHTML =
      '<div class="head"><input type="checkbox" id="c' + i + '" data-h="' + it.h + '" aria-label="Include">' +
      '<label class="t" for="c' + i + '">' + it.t + '</label><span class="pri ' + it.p + '">' + it.p + '</span></div>' +
      '<details><summary>Evidence and fix - ' + it.h + ' h</summary><p><b>Seen:</b> ' + it.ev + '</p><p><b>Fix:</b> ' + it.fix + '</p></details>';
    list.appendChild(d);
  });

  function total() {
    let h = 0;
    list.querySelectorAll('input:checked').forEach((c) => { h += Number(c.dataset.h); });
    document.getElementById('hrs').textContent = h;
    document.getElementById('cost').textContent = '$' + (h * RATE).toLocaleString('en-US');
  }
  list.addEventListener('change', total);

  document.getElementById('filters').addEventListener('click', (e) => {
    const b = e.target.closest('button');
    if (!b) return;
    document.querySelectorAll('#filters button').forEach((x) => x.setAttribute('aria-pressed', x === b));
    list.querySelectorAll('.item').forEach((el) => { el.hidden = b.dataset.f !== 'all' && el.dataset.p !== b.dataset.f; });
  });
  total();
</script>
</body>
</html>
Sample findings with a priority filter. Tick the ones to fix and the hours and cost update.

Not included is where you write the ranking line. Google's guidance for people hiring an SEO says no one can guarantee a number one ranking and to beware of anyone who claims to. Your proposal gains from saying the same.

Attach a file Share link
What the prospect needs To download and open it A browser, no account
Fixing a price after sending Send a new file Edit the code, same link
Copies after a forward One per forward, each as sent One page
Can the reader edit it They hold their own copy No, view only
Size limit Email providers set one, for example 25 MB on personal Gmail Not an attachment
With a file, every forward is its own copy. With a link, everyone opens the same page.
With a file, every forward is its own copy. With a link, everyone opens the same page.

Google's Gmail help, as of October 2026, gives 25 MB for personal accounts and says work and school administrators can set their own limits.

A one-page proposal is far smaller than that, but a file is still a frozen copy. If the prospect will read it on a phone, opening an HTML file on a phone explains what can go wrong with an attached file.

For the quote side of the same job, see how to send a quote to a client.

When it does not work

What you see Cause Fix
An Accept button does nothing Sending a form to a server is blocked End with "reply to this email with your package"
A logo or screenshot is missing The image is hosted elsewhere and outside images are blocked Ask for inline SVG or CSS shapes
A font or script is missing It loads from a site that is not allowed Use Google Fonts, cdnjs, jsDelivr, unpkg or no extras
Numbers you never gave The AI filled a gap Re-run with "use only my notes", then compare line by line
Text is tiny on a phone No viewport meta tag Ask the AI to add one, see viewport meta tag
A price is wrong after sending The page was already shared Edit the code, the same link shows the fix

Questions people ask

What should an SEO proposal include?

A short summary, three to five findings with the evidence you saw, a plan in steps with weeks, one to three priced options, what is not included, and one sentence saying how to say yes. The sample on this page follows that order.

Can I promise a ranking in the proposal?

No. Google's own guidance says no one can guarantee a number one ranking on Google, and it warns about SEOs who promise one. Promise the work you will do and the reports you will send instead.

How should I price it?

Two common shapes are a fixed price per package and hours multiplied by your hourly rate. The second sample on this page ties the price to the findings the prospect ticks. Use whatever you can explain in one sentence, and check your own rates yourself.

Can the prospect click Accept on the page?

Not in a way that sends anything to you. A NOS page cannot send a form to a server, so end the page with a next step they take by email, such as replying with the package they want.

Can the prospect change my prices?

No. A NOS share link opens without an account and cannot be edited by the visitor. Ticking a package on the page changes only what they see on their own screen. You edit the code in your document and the same link shows the new version.

Keep reading