AMPscript 101: Lesson, Cheat Sheet and Playground

Home / MarTech / AMPscript 101
Daniel Robles Interactive lesson Salesforce Marketing Cloud

AMPscript is the scripting language of Salesforce Marketing Cloud. It runs inside emails, SMS, CloudPages and landing pages, and it personalizes content by reading data extensions: tables keyed by subscriber. Learn the essentials, keep the cheat sheet handy, and practice against a sample subscriber below.

Learning Braze too? Try the Liquid 101. Why the two feel so different: Object Oriented Marketing.

1. Lesson 101

Ten concepts cover most real-world AMPscript. Every example on the right is live: edit it and the output updates as you type.

What AMPscript can see

At send time AMPscript sees one row per subscriber from the sendable data extension (the audience), plus any other data extension you look up. There is no profile object: if data is not in a table, AMPscript cannot reach it without an API call.

Delimiters

%%[ ]%% runs code without output. %%= =%% outputs one expression. %%FirstName%% is a shortcut that prints a field from the sendable row.

%%[ SET @greeting = "Hello" ]%%
%%=v(@greeting)=%%, %%FirstName%%!

Variables

Variables start with @. Declare them with VAR (optional) and assign with SET. There are no operators like + or &: everything is a function, such as Concat() or Add().

%%[
  VAR @tier
  SET @tier = Uppercase(AttributeValue("PlanTier"))
]%%
%%=Concat(FirstName, " is on ", @tier)=%%

Reading fields safely

A bare field name like Nickname throws an error if that column does not exist, which stops the send. AttributeValue("Nickname") returns empty instead. Use it whenever a field might be missing.

%%[ SET @nick = AttributeValue("Nickname") ]%%
Nickname: "%%=v(@nick)=%%"

Fallbacks

Combine AttributeValue(), Empty() and IF so a missing value never produces "Hi ,".

%%[
  SET @name = AttributeValue("FirstName")
  IF Empty(@name) THEN SET @name = "there" ENDIF
]%%
Hi %%=v(@name)=%%,

Strings

Concat, Uppercase, ProperCase, Length, Substring, Replace. Positions start at 1, not 0.

%%=Uppercase(Concat(FirstName, " ", LastName))=%%
%%=Substring("Marketing", 1, 6)=%%

Conditionals

IF … THEN / ELSEIF / ELSE / ENDIF. Blocks can open and close around plain HTML, which is how most email templates switch content. String comparison with == is case-insensitive.

%%[ IF PlanTier == "vip" THEN ]%%
  Your VIP concierge is ready.
%%[ ELSEIF PlanTier == "premium" THEN ]%%
  Your premium perks are waiting.
%%[ ELSE ]%%
  Upgrade today.
%%[ ENDIF ]%%

Lookups

Lookup(de, returnField, keyField, keyValue) returns one value from another data extension. Each extra field is another lookup, so keep them few.

%%[
  SET @sku  = AttributeValue("LastSKU")
  SET @name = Lookup("Products_DE", "Name", "SKU", @sku)
]%%
Still thinking about %%=v(@name)=%%?

Rowsets and loops

LookupRows and LookupOrderedRows return a rowset. Walk it with FOR @i = 1 TO RowCount(@rows), Row() and Field(). Rows start at 1.

%%[
  SET @rows = LookupOrderedRows("Orders_DE", 3, "OrderDate desc",
    "SubscriberKey", AttributeValue("SubscriberKey"))
  FOR @i = 1 TO RowCount(@rows) DO
    SET @row = Row(@rows, @i)
]%%
Order %%=Field(@row, "OrderId")=%%
%%[ NEXT @i ]%%

Formatting numbers and dates

.NET-style patterns: FormatNumber(39, "C2") gives $39.00, Format(@date, "dddd, MMMM d") gives Saturday, November 14. DateAdd and DateDiff handle date math.

%%=FormatNumber(AttributeValue("Points"), "N0")=%%
%%=Format(AttributeValue("NextAppointment"), "MMM d, yyyy")=%%

Skipping a subscriber

RaiseError("reason", true) skips this subscriber instead of sending a broken message. Without true it can stop the entire send.

%%[ IF Empty(AttributeValue("LastSKU")) THEN
  RaiseError("No SKU on profile", true)
ENDIF ]%%

Common mistakes

  • Using a bare field name that may not exist. Prefer AttributeValue().
  • Writing = in an IF. Comparisons use ==; = is only for SET.
  • Forgetting ENDIF or NEXT when a block spans HTML.
  • Counting from 0. Substring, IndexOf, Row and loops all start at 1.
  • One Lookup per field on large sends. Each call is a query; fetch a rowset once instead.
  • Assuming journey data is live. Journey entry data is a snapshot taken at entry.

2. Playground

Your code runs against one sample subscriber stored the way SFMC stores it: a sendable row plus related data extensions. Change the subscriber to test your fallbacks. Exercises are graded automatically, and the right panel shows the same task in Liquid so you can feel the difference.

AMPscript Salesforce Marketing Cloud
Rendered output
Same task in Liquid open the Liquid lesson

                      
What AMPscript sees: data extensions

This playground runs a teaching-sized AMPscript interpreter written for this page, not Salesforce's engine. It supports the functions in the cheat sheet. Dates use your browser's time zone.

3. AMPscript cheat sheet

TaskSyntaxNotes
Code block%%[ … ]%%Runs code, outputs nothing by itself
Inline output%%=v(@var)=%%One expression
Personalization string%%FirstName%%Field from the sendable data extension
Comment/* note */Inside a block
Declare / setVAR @a, @b
SET @a = "x"
Variables start with @
Read a field safelyAttributeValue("Field")Empty instead of an error if missing
If / elseIF a == b THEN … ELSEIF … ELSE … ENDIFCan span blocks with HTML in between
LogicAND OR NOT == != < >No arithmetic operators; use functions
LoopFOR @i = 1 TO n DO … NEXT @iAlso DOWNTO
Empty checkEmpty(@a) IsNull(@a)
Inline ifIIF(cond, "yes", "no")
StringsConcat() Uppercase() Lowercase() ProperCase() Length() Substring(s, start, len) IndexOf() Replace() Trim()Positions start at 1
MathAdd() Subtract() Multiply() Divide() Mod()
NumbersFormatNumber(39, "C2") → $39.00
FormatNumber(1250, "N0") → 1,250
C currency, N number, P percent
DatesFormat(@d, "dddd, MMMM d")
FormatDate(@d, "MM/dd/yyyy")
Now() DateAdd(@d, 7, "D") DateDiff(@a, @b, "D")
Units: Y, M, D, H, MI
Lookup one valueLookup("DE", "ReturnField", "KeyField", @key)More key pairs allowed
Lookup rowsLookupRows("DE", "KeyField", @key)
LookupOrderedRows("DE", 3, "Date desc", "KeyField", @key)
Returns a rowset
Walk a rowsetRowCount(@rows) Row(@rows, @i) Field(@row, "Name")Rows start at 1
Output from a blockOutput(v(@a)) OutputLine()
Skip this subscriberRaiseError("reason", true)true skips the subscriber, not the whole send
Not in the playgroundHTTPGet() InsertDE() UpsertDE() ContentBlockByKey() TreatAsContent()Real SFMC functions with side effects or external calls

Official reference: AMPscript guide. Compare with the Liquid cheat sheet.